上回说到了java.util.stream.Stream#forEach
的三个问题:java
java.util.stream.Stream#forEach
是顺序消费吗?java.util.stream.Stream#forEach
是快速失败吗?java.util.stream.Stream#forEach
以前添加元素会怎么样?关于这三个问题的答案,能够点击 Stream#foreach方法摸底三问,你都了解吗 编程
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
方法中,作了两件事情:并发
调用spliterator()
方法,建立Spliterator
对象。在ArrayList
中,其实是建立了ArrayListSpliterator
这个实现类的实例对象。框架
调用StreamSupport.stream(spliterator(), false);
方法。在本示例中,该方法返回了ReferencePipeline.Head
这个实现类的实例对象。源码分析
在java.util.stream.ReferencePipeline.Head#forEach
源码中,首先会判断是否为并行流,若是不是则调用sourceStageSpliterator()
方法获取Spliterator
对象,而后调用java.util.Spliterator#forEachRemaining
方法。性能
也就是说,在顺序流中,java.util.stream.Stream#forEach
方法其实是委托给了java.util.Spliterator#forEachRemaining
方法。spa
什么是Spliterator
呢?线程
Spliterator = Splitting(拆分数据源) + Iterator(迭代数据)
。code
在Spliterator
中主要有如下几个 API:
java.util.Spliterator#trySplit
:该方法返回一个新的Spliterator
对象,用于在多个线程中分别迭代元素,以实现并行处理。java.util.Spliterator#forEachRemaining
:在单个线程中顺序迭代元素。须要注意的是,Spliterator
自己不支持并发编程,它只是提供了一些方法来供开发者使用,要实现并发编程,还须要和 Fork/Join 、线程池之类的框架一块儿使用。
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<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Spliterator<Integer> spliterator = integers.spliterator();
}
复制代码
当调用java.util.ArrayList#spliterator
方法时,实际上是建立了ArrayListSpliterator
对象。
在ArrayList
中有一个内部类:java.util.ArrayList.ArrayListSpliterator
实现了Spliterator
接口。
先来看一下相关的 doc 文档:
ArrayListSpliterator
是一个基于索引的、二分的、懒加载的Spliterator
。
对于可变的List
,主要依靠modCount
来检测并发。同时,为了兼顾性能和并发安全性,相较于ArrayList
,对modCount
的检测是比较保守的。为了实现这个目的,主要作了如下这两件事情:
fence
和expectedModCount
。forEach
操做,只在方法结束时执行ConcurrentModificationException
检查。ArrayListSpliterator
中有三个成员变量:
ArrayList<E> list;
:存放 ArrayList 对象int index
:保存当前索引位置int fence
: 懒加载,直到执行迭代时才会修改,用来记录传入 list 的 sizeint expectedModCount
:懒加载,用来记录 list 的 modCount在Spliterator#forEachRemaining
方法中,将list
引用传给了临时变量list
,同时更新modCount
的值,因此在执行Spliterator#forEachRemaining
方法前,往List
中添加新元素也是能够的。
而对modCount
值的检查正如 doc 中描述的那样,在调用最频繁的forEachRemaining
方法中,为了兼顾性能和并发安全,只会在方法结束时执行ConcurrentModificationException
检查。
ArrayListSpliterator#trySplit
方法的源码也很是简单:
在顺序流中,java.util.stream.Stream#forEach
方法其实是委托给了java.util.Spliterator#forEachRemaining
方法来实现的。
java.util.Spliterator
是JDK8新增的一个接口,相比于java.util.Iterator
接口,该接口不只能够实现顺序迭代集合元素,还能够支持并发编程。
欢迎关注我的公众号: