前言

上回说到了java.util.stream.Stream#forEach的三个问题:

  • java.util.stream.Stream#forEach 是顺序消费吗?
  • java.util.stream.Stream#forEach 是快速失败吗?
  • java.util.stream.Stream#forEach 之前添加元素会怎么样?

关于这三个问题的答案,可以点击 Stream#foreach方法摸底提问,快来回答吧

Stream#forEach 源码解析

public static void main(String[] args) { List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4)); list .stream() .forEach(System.out::println);}

list.stream();方法是调用的Collection中的 default 方法:java.util.Collection#stream:

java.util.Collection#stream

可以看到,java.util.Collection#stream方法中,做了两件事情:

  1. 调用spliterator()方法,创建Spliterator对象。在ArrayList中,实际上是创建了ArrayListSpliterator这个实现类的实例对象。
  1. 调用StreamSupport.stream(spliterator(), false);方法。在本示例中,该方法返回了ReferencePipeline.Head这个实现类的实例对象。

在java.util.stream.ReferencePipeline.Head#forEach源码中,首先会判断是否为并行流,如果不是则调用sourceStageSpliterator()方法获取Spliterator对象,然后调用java.util.Spliterator#forEachRemaining方法。

Stream#forEach

也就是说,在顺序流中,java.util.stream.Stream#forEach方法实际上是委托给了java.util.Spliterator#forEachRemaining方法。

Spliterator

什么是Spliterator呢?

Spliterator = Splitting(拆分数据源) + Iterator(迭代数据)。

在Spliterator中主要有以下几个 API:

  • java.util.Spliterator#trySplit:该方法返回一个新的Spliterator对象,用于在多个线程中分别迭代元素,以实现并行处理。
  • java.util.Spliterator#forEachRemaining:在单个线程中顺序迭代元素。

需要注意的是,Spliterator本身不支持并发编程,它只是提供了一些方法来供开发者使用,要实现并发编程,还需要和 Fork/Join 、线程池之类的框架一起使用。

java.util.List#spliterator

Spliterator VS Iterator

Iterator Spliterator since 1.2 since 1.8 适用于 Collection 适用于 Collection 和 Stream(Map 除外) 不支持并发编程操作 支持并发编程

源码分析

java.util.Spliterator 接口有很多的实现类,本文就以java.util.ArrayList.ArrayListSpliterator为例。

public static void main(String[] args) { List integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6)); Spliterator spliterator = integers.spliterator();}

当调用java.util.ArrayList#spliterator方法时,其实是创建了ArrayListSpliterator对象。

java.util.ArrayList#spliterator

ArrayListSpliterator

在ArrayList中有一个内部类:java.util.ArrayList.ArrayListSpliterator实现了Spliterator接口。

先来看一下相关的 doc 文档:

ArrayListSpliterator

ArrayListSpliterator是一个基于索引的、二分的、懒加载的Spliterator。

对于可变的List,主要依靠modCount来检测并发。同时,为了兼顾性能和并发安全性,相较于ArrayList,对modCount的检测是比较保守的。为了实现这个目的,主要做了以下这两件事情:

  1. 延迟初始化fence和expectedModCount。
  2. 对性能最敏感的forEach操作,只在方法结束时执行ConcurrentModificationException检查。

构造器和成员变量

Spliterator构造器

ArrayListSpliterator 中有三个成员变量:

  • ArrayList list;:存放 ArrayList 对象
  • int index:保存当前索引位置
  • int fence: 懒加载,直到执行迭代时才会修改,用来记录传入 list 的 size
  • int expectedModCount:懒加载,用来记录 list 的 modCount

ArrayListSpliterator#forEachRemaining

forEachRemaining

在Spliterator#forEachRemaining方法中,将list引用传给了临时变量list,同时更新modCount的值,所以在执行Spliterator#forEachRemaining方法前,往List中添加新元素也是可以的。

而对modCount值的检查正如 doc 中描述的那样,在调用最频繁的forEachRemaining方法中,为了兼顾性能和并发安全,只会在方法结束时执行ConcurrentModificationException检查。

ArrayListSpliterator#trySplit

ArrayListSpliterator#trySplit方法的源码也非常简单:

ArrayListSpliterator#trySplit

总结

在顺序流中,java.util.stream.Stream#forEach方法实际上是委托给了java.util.Spliterator#forEachRemaining方法来实现的。

java.util.Spliterator是 JDK8 新增的一个接口,相比于java.util.Iterator接口,该接口不仅可以实现顺序迭代集合元素,还可以支持并发编程。

.foreach()需要判断空吗_这次我们来聊聊 Stream#forEach 源码相关推荐

  1. 企业微信SCRM系统部署_企业微信SCRM二次开发_企业微信SCRM系统独立版源码价格

    企业微信SCRM系统部署_企业微信SCRM二次开发_企业微信SCRM系统独立版源码价格 点趣互动是企业微信系统的第三方应用提供厂商,用于管理员工企业微信的内一款系统软件.点趣互动企业微信scrm软件主 ...

  2. springboot 事务_原创002 | 搭上SpringBoot事务源码分析专车

    前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为什么 ...

  3. python records库_你的第一份Python库源码阅读:records库

    基本介绍 records是kennethreitz的for Humans™系列,使用原生sql去操作大多数的关系型数据库(Postgresql, MySQL, SQLite, Oracle和 MS-S ...

  4. Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析

    文章目录 Pre 实现原理 应用 配置类 Event事件 事件监听 EventListener 发布事件 publishEvent 源码解析 (反推) Spring默认的事件广播器 SimpleApp ...

  5. android tcp socket框架_最流行的 Web 框架 Gin 源码阅读

    最近公司大部分项目开始往golang换, api的框架选定使用gin, 于是将 gin的源码看了一遍, 会用几篇文章将gin的流程及流程做一个梳理, 下面进入正题. gin框架预览 上图大概是 gin ...

  6. java tomcat源码_详解Tomcat系列(一)-从源码分析Tomcat的启动

    在整个Tomcat系列文章讲解之前, 我想说的是虽然整个Tomcat体系比较复杂, 但是Tomcat中的代码并不难读, 只要认真花点功夫, 一定能啃下来. 由于篇幅的原因, 很难把Tomcat所有的知 ...

  7. 机票预定系统类图_电商系统延时任务机制源码分享

    需求分析: 在javashop电商系统中,各种促销活动都有开始时间和结束时间,想要让一个活动在预定的时间开始或结束,使用定时任务轮询,存在耗性能并且不能在准确的时间点开始或结束的缺点,为了可以在指定的 ...

  8. access用扫描枪输入_判断是否扫码枪输入的通用函数源码

    [Access源码]判断是否扫码枪输入的通用函数源码分享. 现在在仓库管理,超市贩售等场合,扫码枪等扫码输入设备已经成了必不可少的工具,基本上不再需要人工去输入商品编码了. 那么我们在用Access开 ...

  9. 易语言lsp劫持_易语言网截插件修复源码

    易语言网截插件修复源码.版本 2 .支持库 shell .支持库 eNetIntercept .子程序 _按钮1_被单击 写到文件 (取特定目录 (10) + "/lsp.bat" ...

最新文章

  1. python学习之pip常用命令
  2. 研究生被录取后放导师鸽子,学校要上报教育部失信名单取消其推免资格
  3. 计算机组成原理课程内容,计算机组成原理课程教学大纲.doc.doc
  4. Linux 命令之 pwck -- 用来验证系统认证文件内容和格式的完整性
  5. 邓总的vim配置,需要的自己拿走~
  6. 怎么查看任天堂账号是哪个服务器的,科普:任天堂账号和NS的本地用户有什么区别?...
  7. mysql ageval 1 30_通过sqoop eval传递mysql属性
  8. LiveReload for mac 软件下载
  9. 计算机鼠标左右键作用,win7电脑鼠标右键有什么功能和作用
  10. 29-地理空间数据云下载【进阶】
  11. 脚本精灵服务器引擎数据为空,脚本精灵服务器
  12. win7下VS2012配置DirectShow+Opencv并且多摄像头采集
  13. 使用Python和机器学习进行文本情感分类
  14. 10杯水只有一滴有毒,用四只老鼠测试,二进制的方法快速找出哪瓶有毒;
  15. git 撤销单个文件到某个提交
  16. 人工智能技术与专利技术变革
  17. JSRUN有什么用?
  18. 数学无敌—王老菊教你当典狱长
  19. 在html中雪碧图的坐标怎么看,详解CSS Sprite雪碧图的应用
  20. 清除电脑各种使用记录不留痕迹,保护你的隐私!

热门文章

  1. Mongo数据库慢查询功能
  2. Jenkins多环境持续集成架构实践
  3. Ubuntu中ssh远程报错:packet_write_wait: Connection to 192.168.163.190 port 22: Broken pipe lost connection
  4. Eclipse,提交代码,版本比较时,不忽略空格
  5. win7下,令人头疼的 classpnp.sys (附带:安装系统时蓝屏;0x0000007b)。
  6. 【Linux】awk处理变量
  7. vue-cli3 中 sockjs-node/info?t=报错 的解决方法
  8. WCF+Restfull服务 提交或获取数据时数据大小限制问题解决方案
  9. BFS-迷宫问题-用宽度(广度)优先搜索解决最优路径问题
  10. flutter initializing gradle终极解决方案