上回說到了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
接口,該接口不只能夠實現順序迭代集合元素,還能夠支持併發編程。
歡迎關注我的公衆號: