【精通函数式编程】(四)流-Stream API原理解析

简介: 本文通过集合引出Stream流,主要讲解了流的基本概念、使用的原理,Stream流水线的运行原理

image.png

前言:

📫 作者简介:小明 java 问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫

🏆 Java 领域优质创作者、阿里云专家博主、华为云享专家🏆

🔥 如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主哦

本文导读

集合是java中使用最多的数据结构,我们如何处理大量元素就是个问题,多线程(线程池)+迭代器性能是还可以,但是太麻烦,也不利于开发和管理,并发的问题还要考虑,这个时候为了保证java的流行性,或者说为了不让java淘汰,在高版本搞出来 流(Stream)。

一、集合与流

集合是java中使用最多的数据结构,包括List、Map,面向对象编程更像是面向集合面向数据结构编程,当我们要处理大量元素的时候,往往会使用多线程,多线程还不能直接创建,要使用线程池创建,麻烦的一批(Java开发规范);

我们如何处理大量元素就是个问题,多线程(线程池)+迭代器性能是还可以,但是太麻烦,也不利于开发和管理,并发的问题还要考虑,这个时候为了保证java的流行性,或者说为了不让java淘汰,在高版本搞出来 流(Stream)。

流(Stream)是声明式处理集合的,我们可以把他当做一个高级的 迭代器+多线程容器,他不能简单理解为一个 流 数据结构,集合(List、Map)可以增删改查,虽然流可以实现诸如过滤、合并,分组等等操作,但是其元素是 按需计算,这其实是一种生产者-消费者模式,流就像一个 正在创建的集合,他会按要求变化后计算值。

下面三段代码,for循环遍历list + if、list转迭代器 + if、等同逻辑

// for + if
        List<BigDecimal> orderAmt = new ArrayList<>();
        for (OrderInfo orderInfo : orderInfos) {
            if (orderInfo.getOrderAmt().compareTo(BigDecimal.ZERO) > 0) {
                orderAmt.add(orderInfo.getOrderAmt());
            }
        }
        // 迭代器 + if
        Iterator<OrderInfo> it = orderInfos.iterator();
        while (it.hasNext()) {
            OrderInfo orderInfo = it.next();
            if (orderInfo.getOrderAmt().compareTo(BigDecimal.ZERO) > 0) {
                orderAmt.add(orderInfo.getOrderAmt());
            }
        }
        // Stream流 + Lambda表达式
        List<BigDecimal> collect = orderInfos.stream()
                .filter(orderInfo -> orderInfo.getOrderAmt().compareTo(BigDecimal.ZERO) > 0)
                .map(orderInfo -> orderInfo.getOrderAmt()).collect(Collectors.toList());

二、什么是流

流是Java 高版本的API,是声明式处理集合的,声明式(简洁)就是说我想要做什么而不是如何实现,把他当做一个高级的 迭代器(可复合-灵活)+多线程容器(可并行-性能好)

要了解流是什么,还要了解他的定义、类路径、如何使用,以及一些使用规范

流在 java.util.stream.Stream 接口中定义,我们可以看到接口注释中有大量讲解,我们就根据这些注释学习流

我们看源码,该类里面有大量方法接口(就和集合一样),可以访问元素,但是集合是数据结构,所以他主要目的是使用特定算法和数据结构存储/访问元素;但流的目的是计算。Stream<T> 流的接收元素是泛型,流会使用一个数据源(这个数据源流不会改变顺序)

image.png

注释中说了,1、流是懒加载原则;仅当 端操作启动时才对源数据执行计算,并且仅在需要时消耗源元素;2、流不会改变原有的数据源;3、流只操作一次;4、可能会抛出 IllegalStateException;5、流不需要被关闭;6、流是内部迭代。详细论证我们放到下面

三、流的使用原则

流使用包括3件事,要有数据源来执行操作,要有一个链这个链试试中间操作的步骤,一个终端操作生成结果。下面代码会论证 这些原则以及上述原理

流只能遍历一次,且是按顺序遍历(流是按顺序遍历的,不会一个List都过滤完,才map,而是每个元素都是流水线执行的)

// 流是按顺序遍历的,不会一个List都过滤完,才map,而是每个元素都是流水线执行的
List<BigDecimal> collect = orderInfos.stream().filter(orderInfo -> {
    System.out.println("===filter" + orderInfo.getOrderAmt());
    return orderInfo.getOrderAmt().compareTo(BigDecimal.ZERO) > 0;
}).map(orderInfo -> {
    System.out.println("===map" + orderInfo.getOrderAmt());
    return orderInfo.getOrderAmt();
}).collect(Collectors.toList());
System.out.println(collect);
// 流只能操作一次,当再次使用的时候会报错IllegalStateException:operated upon or closed
Stream<BigDecimal> stream = collect.stream();
stream.forEach(System.out::print);
System.out.println("===========");
stream.forEach(System.out::print);

image.png

流,java.util.stream.Stream 接口中定义 很多方法,这些方法分为 两大类。filter、map、limit、forEach、peek 等,可以连成流水线;collect 触发流水线执行并关闭(终端操作)。我们把连接起来称为中间操作,关闭流的称为终端操作。一个流的使用原则就是要有数据源、中间操作、终端从操作

四、流的运行原理

为什么Stram流、Lamda表达式式写法又叫作函数式编程?一是调用手法像是函数一般,只须传入参数即可调用;二是Lamda实现方式为生出静态函数调用而成

下图执行步骤标号 1 2 3 4,那流是如何 解决 Stream流水线的?

image.png

Stream中用某种实例化后的PipelineHelper来代表Stage,将具有先后顺序的各个Stage连到一起,就构成了整个流水线。跟Stream相关类和接口的继承关系图示。

image.png

上图中Head用于表示第一个Stage,即调用调用诸如Collection.stream()方法产生的Stage,很显然这个Stage里不包含任何操作;StatelessOp和StatefulOp分别表示无状态和有状态的Stage,对应于无状态和有状态的中间操作。

下图中通过Collection.stream()方法得到Head也就是stg0,紧接着调用一系列的中间操作,不断产生新的Stream。这些Stream对象以双向链表的形式组织在一起,构成整个流水线,由于每个Stage都记录了前一个Stage和本次的操作以及回调函数,依靠这种结构就能建立起对数据源的所有操作。这就是Stream记录操作的方式。

image.png

小结

本文通过集合引出Stream流,主要讲解了流的基本概念、使用的原理,Stream流水线的运行原理。

相关文章
|
1月前
|
API 开发者 数据采集
高效获取淘宝商品详情:API 开发实现链接解析的完整技术方案
2025反向海淘新机遇:依托代购系统,聚焦小众垂直品类,结合Pandabay数据选品,降本增效。系统实现智能翻译、支付风控、物流优化,助力中式养生茶等品类利润翻倍,新手也能快速入局全球市场。
高效获取淘宝商品详情:API 开发实现链接解析的完整技术方案
|
1月前
|
数据采集 存储 供应链
第三方电商数据 API 数据来源深度解析:合规与稳定背后的核心逻辑
本文揭秘第三方电商数据API的底层逻辑:通过官方授权、生态共享与合规采集三重来源,结合严格清洗校验,确保数据稳定、合规、高质。企业选型应关注来源合法性与场景匹配度,避开数据陷阱,实现真正数据驱动增长
|
1月前
|
存储 监控 安全
132_API部署:FastAPI与现代安全架构深度解析与LLM服务化最佳实践
在大语言模型(LLM)部署的最后一公里,API接口的设计与安全性直接决定了模型服务的可用性、稳定性与用户信任度。随着2025年LLM应用的爆炸式增长,如何构建高性能、高安全性的REST API成为开发者面临的核心挑战。FastAPI作为Python生态中最受青睐的Web框架之一,凭借其卓越的性能、强大的类型安全支持和完善的文档生成能力,已成为LLM服务化部署的首选方案。
|
1月前
|
存储 缓存 算法
淘宝买家秀 API 深度开发:多模态内容解析与合规推荐技术拆解
本文详解淘宝买家秀接口(taobao.reviews.get)的合规调用、数据标准化与智能推荐全链路方案。涵盖权限申请、多模态数据清洗、情感分析、混合推荐模型及缓存优化,助力开发者提升审核效率60%、商品转化率增长28%,实现UGC数据高效变现。
|
1月前
|
XML 数据采集 API
用Lxml高效解析XML格式数据:以天气API为例
免费Python教程:实战解析中国天气网XML数据,详解Lxml库高效解析技巧、XPath用法、流式处理大文件及IP封禁应对策略,助你构建稳定数据采集系统。
165 0
|
2月前
|
安全 NoSQL API
拼多多:通过微信支付API实现社交裂变付款的技术解析
基于微信JSAPI构建社交裂变支付系统,用户发起拼单后生成预订单与分享链接,好友代付后通过回调更新订单并触发奖励。集成微信支付、异步处理、签名验签与Redis关系绑定,提升支付成功率与裂变系数,实现高效安全的闭环支付。
415 0
|
8月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
825 29
|
8月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
326 4
|
8月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

推荐镜像

更多
  • DNS
  • 下一篇
    oss云网关配置