.foreach()需要判断空吗_这次我们来聊聊 Stream#forEach 源码
前言
上回说到了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方法中,做了两件事情:
- 调用spliterator()方法,创建Spliterator对象。在ArrayList中,实际上是创建了ArrayListSpliterator这个实现类的实例对象。
- 调用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的检测是比较保守的。为了实现这个目的,主要做了以下这两件事情:
- 延迟初始化fence和expectedModCount。
- 对性能最敏感的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 源码相关推荐
- 企业微信SCRM系统部署_企业微信SCRM二次开发_企业微信SCRM系统独立版源码价格
企业微信SCRM系统部署_企业微信SCRM二次开发_企业微信SCRM系统独立版源码价格 点趣互动是企业微信系统的第三方应用提供厂商,用于管理员工企业微信的内一款系统软件.点趣互动企业微信scrm软件主 ...
- springboot 事务_原创002 | 搭上SpringBoot事务源码分析专车
前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为什么 ...
- python records库_你的第一份Python库源码阅读:records库
基本介绍 records是kennethreitz的for Humans™系列,使用原生sql去操作大多数的关系型数据库(Postgresql, MySQL, SQLite, Oracle和 MS-S ...
- Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析
文章目录 Pre 实现原理 应用 配置类 Event事件 事件监听 EventListener 发布事件 publishEvent 源码解析 (反推) Spring默认的事件广播器 SimpleApp ...
- android tcp socket框架_最流行的 Web 框架 Gin 源码阅读
最近公司大部分项目开始往golang换, api的框架选定使用gin, 于是将 gin的源码看了一遍, 会用几篇文章将gin的流程及流程做一个梳理, 下面进入正题. gin框架预览 上图大概是 gin ...
- java tomcat源码_详解Tomcat系列(一)-从源码分析Tomcat的启动
在整个Tomcat系列文章讲解之前, 我想说的是虽然整个Tomcat体系比较复杂, 但是Tomcat中的代码并不难读, 只要认真花点功夫, 一定能啃下来. 由于篇幅的原因, 很难把Tomcat所有的知 ...
- 机票预定系统类图_电商系统延时任务机制源码分享
需求分析: 在javashop电商系统中,各种促销活动都有开始时间和结束时间,想要让一个活动在预定的时间开始或结束,使用定时任务轮询,存在耗性能并且不能在准确的时间点开始或结束的缺点,为了可以在指定的 ...
- access用扫描枪输入_判断是否扫码枪输入的通用函数源码
[Access源码]判断是否扫码枪输入的通用函数源码分享. 现在在仓库管理,超市贩售等场合,扫码枪等扫码输入设备已经成了必不可少的工具,基本上不再需要人工去输入商品编码了. 那么我们在用Access开 ...
- 易语言lsp劫持_易语言网截插件修复源码
易语言网截插件修复源码.版本 2 .支持库 shell .支持库 eNetIntercept .子程序 _按钮1_被单击 写到文件 (取特定目录 (10) + "/lsp.bat" ...
最新文章
- python学习之pip常用命令
- 研究生被录取后放导师鸽子,学校要上报教育部失信名单取消其推免资格
- 计算机组成原理课程内容,计算机组成原理课程教学大纲.doc.doc
- Linux 命令之 pwck -- 用来验证系统认证文件内容和格式的完整性
- 邓总的vim配置,需要的自己拿走~
- 怎么查看任天堂账号是哪个服务器的,科普:任天堂账号和NS的本地用户有什么区别?...
- mysql ageval 1 30_通过sqoop eval传递mysql属性
- LiveReload for mac 软件下载
- 计算机鼠标左右键作用,win7电脑鼠标右键有什么功能和作用
- 29-地理空间数据云下载【进阶】
- 脚本精灵服务器引擎数据为空,脚本精灵服务器
- win7下VS2012配置DirectShow+Opencv并且多摄像头采集
- 使用Python和机器学习进行文本情感分类
- 10杯水只有一滴有毒,用四只老鼠测试,二进制的方法快速找出哪瓶有毒;
- git 撤销单个文件到某个提交
- 人工智能技术与专利技术变革
- JSRUN有什么用?
- 数学无敌—王老菊教你当典狱长
- 在html中雪碧图的坐标怎么看,详解CSS Sprite雪碧图的应用
- 清除电脑各种使用记录不留痕迹,保护你的隐私!
热门文章
- Mongo数据库慢查询功能
- Jenkins多环境持续集成架构实践
- Ubuntu中ssh远程报错:packet_write_wait: Connection to 192.168.163.190 port 22: Broken pipe lost connection
- Eclipse,提交代码,版本比较时,不忽略空格
- win7下,令人头疼的 classpnp.sys (附带:安装系统时蓝屏;0x0000007b)。
- 【Linux】awk处理变量
- vue-cli3 中 sockjs-node/info?t=报错 的解决方法
- WCF+Restfull服务 提交或获取数据时数据大小限制问题解决方案
- BFS-迷宫问题-用宽度(广度)优先搜索解决最优路径问题
- flutter initializing gradle终极解决方案