前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >这次我们来简单聊聊 Stream#forEach 源码

这次我们来简单聊聊 Stream#forEach 源码

作者头像
Coder小黑
发布2019-12-18 15:04:39
8460
发布2019-12-18 15:04:39
举报
文章被收录于专栏:Coder小黑

前言

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

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

关于这三个问题的答案,可以点击阅读原文查看哦。

Stream#forEach 源码解析

代码语言:javascript
复制
public static void main(String[] args) {
  List<Integer> 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这个实现类的实例对象。
  2. 调用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为例。

代码语言:javascript
复制
public static void main(String[] args) {
  List<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
  Spliterator<Integer> 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. 延迟初始化fenceexpectedModCount
  2. 对性能最敏感的forEach操作,只在方法结束时执行ConcurrentModificationException检查。

构造器和成员变量

Spliterator构造器

ArrayListSpliterator 中有四个成员变量:

  • ArrayList<E> 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接口,该接口不仅可以实现顺序迭代集合元素,还可以支持并发编程。

你点的每个好看,我都认真当成了喜欢

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coder小黑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Stream#forEach 源码解析
  • Spliterator
  • Spliterator VS Iterator
  • 源码分析
    • ArrayListSpliterator
      • 构造器和成员变量
        • ArrayListSpliterator#forEachRemaining
          • ArrayListSpliterator#trySplit
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档