此次咱們來簡單聊聊 Stream#forEach 源碼

前言

上回說到了java.util.stream.Stream#forEach的三個問題:java

  • 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<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方法。spa

Spliterator

什麼是Spliterator呢?線程

Spliterator = Splitting(拆分數據源) + Iterator(迭代數據)code

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<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接口,該接口不只能夠實現順序迭代集合元素,還能夠支持併發編程。


歡迎關注我的公衆號:

Coder小黑
相關文章
相關標籤/搜索