前提
Stream
是JDK1.8
中首次引入的,距今已經過去了接近8
年時間(JDK1.8
正式版是2013
年底釋出的)。Stream
的引入一方面極大地簡化了某些開發場景,另一方面也可能降低了編碼的可讀性(確實有不少人說到Stream
會降低程式碼的可讀性,但是在筆者看來,熟練使用之後反而覺得程式碼的可讀性提高了)。這篇文章會花巨量篇幅,詳細分析Stream
的底層實現原理,參考的原始碼是JDK11
的原始碼,其他版本JDK
可能不適用於本文中的原始碼展示和相關例子。
這篇文章花費了極多時間和精力梳理和編寫,希望能夠幫助到本文的讀者
Stream是如何做到向前相容的
Stream
是JDK1.8
引入的,如要需要JDK1.7
或者以前的程式碼也能在JDK1.8
或以上執行,那麼Stream
的引入必定不能在原來已經發布的介面方法進行修改,否則必定會因為相容性問題導致老版本的介面實現無法在新版本中執行(方法簽名出現異常),猜測是基於這個問題引入了介面預設方法,也就是default
關鍵字。檢視原始碼可以發現,ArrayList
的超類Collection
和Iterable
分別新增了數個default
方法:
// java.util.Collection部分原始碼
public interface Collection<E> extends Iterable<E> {
// 省略其他程式碼
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
// java.lang.Iterable部分原始碼
public interface Iterable<T> {
// 省略其他程式碼
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
從直覺來看,這些新增的方法應該就是Stream
實現的關鍵方法(後面會印證這不是直覺,而是檢視原始碼的結果)。介面預設方法在使用上和例項方法一致,在實現上可以直接在介面方法中編寫方法體,有點靜態方法的意味,但是子類可以覆蓋其實現(也就是介面預設方法在本介面中的實現有點像靜態方法,可以被子類覆蓋,使用上和例項方法一致)。這種實現方式,有可能是一種突破,也有可能是一種妥協,但是無論是妥協還是突破,都實現了向前相容:
// JDK1.7中的java.lang.Iterable
public interface Iterable<T> {
Iterator<T> iterator();
}
// JDK1.7中的Iterable實現
public MyIterable<Long> implements Iterable<Long>{
public Iterator<Long> iterator(){
....
}
}
如上,MyIterable
在JDK1.7
中定義,如果該類在JDK1.8
中執行,那麼呼叫其例項中的forEach()
和spliterator()
方法,相當於直接呼叫JDK1.8
中的Iterable
中的介面預設方法forEach()
和spliterator()
。當然受限於JDK
版本,這裡只能確保編譯通過,舊功能正常使用,而無法在JDK1.7
中使用Stream
相關功能或者使用default
方法關鍵字。總結這麼多,就是想說明為什麼使用JDK7
開發和編譯的程式碼可以在JDK8
環境下執行。
可拆分迭代器Spliterator
Stream
實現的基石是Spliterator
,Spliterator
是splitable iterator
的縮寫,意為"可拆分迭代器",用於遍歷指定資料來源(例如陣列、集合或者IO Channel
等)中的元素,在設計上充分考慮了序列和並行的場景。上一節提到了Collection
存在介面預設方法spliterator()
,此方法會生成一個Spliterator<E>
例項,意為著所有的集合子類都具備建立Spliterator
例項的能力。Stream
的實現在設計上和Netty
中的ChannelHandlerContext
十分相似,本質是一個連結串列,而Spliterator
就是這個連結串列的Head
節點(Spliterator
例項就是一個流例項的頭節點,後面分析具體的原始碼時候再具體展開)。
Spliterator介面方法
接著看Spliterator
介面定義的方法:
public interface Spliterator<T> {
// 暫時省略其他程式碼
boolean tryAdvance(Consumer<? super T> action);
default void forEachRemaining(Consumer<? super T> action) {
do { } while (tryAdvance(action));
}
Spliterator<T> trySplit();
long estimateSize();
default long getExactSizeIfKnown() {
return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
}
int characteristics();
default boolean hasCharacteristics(int characteristics) {
return (characteristics() & characteristics) == characteristics;
}
default Comparator<? super T> getComparator() {
throw new IllegalStateException();
}
// 暫時省略其他程式碼
}
tryAdvance
- 方法簽名:
boolean tryAdvance(Consumer<? super T> action)
- 功能:如果
Spliterator
中存在剩餘元素,則對其中的某個元素執行傳入的action
回撥,並且返回true
,否則返回false
。如果Spliterator
啟用了ORDERED
特性,會按照順序(這裡的順序值可以類比為ArrayList
中容器陣列元素的下標,ArrayList
中新增新元素是天然有序的,下標由零開始遞增)處理下一個元素 - 例子:
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
Spliterator<Integer> spliterator = list.stream().spliterator();
final AtomicInteger round = new AtomicInteger(1);
final AtomicInteger loop = new AtomicInteger(1);
while (spliterator.tryAdvance(num -> System.out.printf("第%d輪迴調Action,值:%d\n", round.getAndIncrement(), num))) {
System.out.printf("第%d輪迴圈\n", loop.getAndIncrement());
}
}
// 控制檯輸出
第1輪迴調Action,值:2
第1輪迴圈
第2輪迴調Action,值:1
第2輪迴圈
第3輪迴調Action,值:3
第3輪迴圈
forEachRemaining
- 方法簽名:
default void forEachRemaining(Consumer<? super T> action)
- 功能:如果
Spliterator
中存在剩餘元素,則對其中的所有剩餘元素在當前執行緒中執行傳入的action
回撥。如果Spliterator
啟用了ORDERED
特性,會按照順序處理剩餘所有元素。這是一個介面預設方法,方法體比較粗暴,直接是一個死迴圈包裹著tryAdvance()
方法,直到false
退出迴圈 - 例子:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
Spliterator<Integer> spliterator = list.stream().spliterator();
final AtomicInteger round = new AtomicInteger(1);
spliterator.forEachRemaining(num -> System.out.printf("第%d輪迴調Action,值:%d\n", round.getAndIncrement(), num));
}
// 控制檯輸出
第1輪迴調Action,值:2
第2輪迴調Action,值:1
第3輪迴調Action,值:3
trySplit
- 方法簽名:
Spliterator<T> trySplit()
- 功能:如果當前的
Spliterator
是可分割槽(可分割)的,那麼此方法將會返回一個全新的Spliterator
例項,這個全新的Spliterator
例項裡面的元素不會被當前Spliterator
例項中的元素覆蓋(這裡是直譯了API
註釋,實際要表達的意思是:當前的Spliterator
例項X
是可分割的,trySplit()
方法會分割X
產生一個全新的Spliterator
例項Y
,原來的X
所包含的元素(範圍)也會收縮,類似於X = [a,b,c,d] => X = [a,b], Y = [c,d]
;如果當前的Spliterator
例項X
是不可分割的,此方法會返回NULL
),具體的分割演算法由實現類決定 - 例子:
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
list.add(1);
Spliterator<Integer> first = list.stream().spliterator();
Spliterator<Integer> second = first.trySplit();
first.forEachRemaining(num -> {
System.out.printf("first spliterator item: %d\n", num);
});
second.forEachRemaining(num -> {
System.out.printf("second spliterator item: %d\n", num);
});
}
// 控制檯輸出
first spliterator item: 4
first spliterator item: 1
second spliterator item: 2
second spliterator item: 3
estimateSize
- 方法簽名:
long estimateSize()
- 功能:返回
forEachRemaining()
方法需要遍歷的元素總量的估計值,如果樣本個數是無限、計算成本過高或者未知,會直接返回Long.MAX_VALUE
- 例子:
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
list.add(1);
Spliterator<Integer> spliterator = list.stream().spliterator();
System.out.println(spliterator.estimateSize());
}
// 控制檯輸出
4
getExactSizeIfKnown
- 方法簽名:
default long getExactSizeIfKnown()
- 功能:如果當前的
Spliterator
具備SIZED
特性(關於特性,下文再展開分析),那麼直接呼叫estimateSize()
方法,否則返回-1
- 例子:
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
list.add(1);
Spliterator<Integer> spliterator = list.stream().spliterator();
System.out.println(spliterator.getExactSizeIfKnown());
}
// 控制檯輸出
4
int characteristics()
- 方法簽名:
long estimateSize()
- 功能:當前的
Spliterator
具備的特性(集合),採用位運算,儲存在32
位整數中(關於特性,下文再展開分析)
hasCharacteristics
- 方法簽名:
default boolean hasCharacteristics(int characteristics)
- 功能:判斷當前的
Spliterator
是否具備傳入的特性
getComparator
- 方法簽名:
default Comparator<? super T> getComparator()
- 功能:如果當前的
Spliterator
具備SORTED
特性,則需要返回一個Comparator
例項;如果Spliterator
中的元素是天然有序(例如元素實現了Comparable
介面),則返回NULL
;其他情況直接丟擲IllegalStateException
異常
Spliterator自分割
Spliterator#trySplit()
可以把一個既有的Spliterator
例項分割為兩個Spliterator
例項,筆者這裡把這種方式稱為Spliterator
自分割,示意圖如下:
這裡的分割在實現上可以採用兩種方式:
- 物理分割:對於
ArrayList
而言,把底層陣列拷貝並且進行分割,用上面的例子來說相當於X = [1,3,4,2] => X = [4,2], Y = [1,3]
,這樣實現加上對於ArrayList
中本身的元素容器陣列,相當於多存了一份資料,顯然不是十分合理 - 邏輯分割:對於
ArrayList
而言,由於元素容器陣列天然有序,可以採用陣列的索引(下標)進行分割,用上面的例子來說相當於X = 索引表[0,1,2,3] => X = 索引表[2,3], Y = 索引表[0,1]
,這種方式是共享底層容器陣列,只對元素索引進行分割,實現上比較簡單而且相對合理
參看ArrayListSpliterator
的原始碼,可以分析其分割演算法實現:
// ArrayList#spliterator()
public Spliterator<E> spliterator() {
return new ArrayListSpliterator(0, -1, 0);
}
// ArrayList中內部類ArrayListSpliterator
final class ArrayListSpliterator implements Spliterator<E> {
// 當前的處理的元素索引值,其實是剩餘元素的下邊界值(包含),在tryAdvance()或者trySplit()方法中被修改,一般初始值為0
private int index;
// 柵欄,其實是元素索引值的上邊界值(不包含),一般初始化的時候為-1,使用時具體值為元素索引值上邊界加1
private int fence;
// 預期的修改次數,一般初始化值等於modCount
private int expectedModCount;
ArrayListSpliterator(int origin, int fence, int expectedModCount) {
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
// 獲取元素索引值的上邊界值,如果小於0,則把hi和fence都賦值為(ArrayList中的)size,expectedModCount賦值為(ArrayList中的)modCount,返回上邊界值
// 這裡注意if條件中有賦值語句hi = fence,也就是此方法呼叫過程中臨時變數hi總是重新賦值為fence,fence是ArrayListSpliterator例項中的成員屬性
private int getFence() {
int hi;
if ((hi = fence) < 0) {
expectedModCount = modCount;
hi = fence = size;
}
return hi;
}
// Spliterator自分割,這裡採用了二分法
public ArrayListSpliterator trySplit() {
// hi等於當前ArrayListSpliterator例項中的fence變數,相當於獲取剩餘元素的上邊界值
// lo等於當前ArrayListSpliterator例項中的index變數,相當於獲取剩餘元素的下邊界值
// mid = (lo + hi) >>> 1,這裡的無符號右移動1位運算相當於(lo + hi)/2
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
// 當lo >= mid的時候為不可分割,返回NULL,否則,以index = lo,fence = mid和expectedModCount = expectedModCount建立一個新的ArrayListSpliterator
// 這裡有個細節之處,在新的ArrayListSpliterator構造引數中,當前的index被重新賦值為index = mid,這一點容易看漏,老程式設計師都喜歡做這樣的賦值簡化
// lo >= mid返回NULL的時候,不會建立新的ArrayListSpliterator,也不會修改當前ArrayListSpliterator中的引數
return (lo >= mid) ? null : new ArrayListSpliterator(lo, index = mid, expectedModCount);
}
// tryAdvance實現
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null)
throw new NullPointerException();
// 獲取迭代的上下邊界
int hi = getFence(), i = index;
// 由於前面分析下邊界是包含關係,上邊界是非包含關係,所以這裡要i < hi而不是i <= hi
if (i < hi) {
index = i + 1;
// 這裡的elementData來自ArrayList中,也就是前文經常提到的元素陣列容器,這裡是直接通過元素索引訪問容器中的資料
@SuppressWarnings("unchecked") E e = (E)elementData[i];
// 對傳入的Action進行回撥
action.accept(e);
// 併發修改異常判斷
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
// forEachRemaining實現,這裡沒有采用預設實現,而是完全覆蓋實現一個新方法
public void forEachRemaining(Consumer<? super E> action) {
// 這裡會新建所需的中間變數,i為index的中間變數,hi為fence的中間變數,mc為expectedModCount的中間變數
int i, hi, mc;
Object[] a;
if (action == null)
throw new NullPointerException();
// 判斷容器陣列存在性
if ((a = elementData) != null) {
// hi、fence和mc初始化
if ((hi = fence) < 0) {
mc = modCount;
hi = size;
}
else
mc = expectedModCount;
// 這裡就是先做引數合法性校驗,再遍歷臨時陣列容器a中中[i,hi)的剩餘元素對傳入的Action進行回撥
// 這裡注意有一處隱蔽的賦值(index = hi),下界被賦值為上界,意味著每個ArrayListSpliterator例項只能呼叫一次forEachRemaining()方法
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
// 這裡校驗ArrayList的modCount和mc是否一致,理論上在forEachRemaining()遍歷期間,不能對陣列容器進行元素的新增或者移除,一旦發生modCount更變會丟擲異常
if (modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
// 獲取剩餘元素估計值,就是用剩餘元素索引上邊界直接減去下邊界
public long estimateSize() {
return getFence() - index;
}
// 具備ORDERED、SIZED和SUBSIZED特性
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
在閱讀原始碼的時候務必注意,老一輩的程式設計師有時候會採用比較隱蔽的賦值方式,筆者認為需要展開一下:
第一處紅圈位置在構建新的ArrayListSpliterator
的時候,當前ArrayListSpliterator
的index
屬性也被修改了,過程如下圖:
第二處紅圈位置,在forEachRemaining()
方法呼叫時候做引數校驗,並且if
分支裡面把index
(下邊界值)賦值為hi
(上邊界值),那麼一個ArrayListSpliterator
例項中的forEachRemaining()
方法的遍歷操作必定只會執行一次。可以這樣驗證一下:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
Spliterator<Integer> spliterator = list.stream().spliterator();
final AtomicInteger round = new AtomicInteger(1);
spliterator.forEachRemaining(num -> System.out.printf("[第一次遍歷forEachRemaining]第%d輪迴調Action,值:%d\n", round.getAndIncrement(), num));
round.set(1);
spliterator.forEachRemaining(num -> System.out.printf("[第二次遍歷forEachRemaining]第%d輪迴調Action,值:%d\n", round.getAndIncrement(), num));
}
// 控制檯輸出
[第一次遍歷forEachRemaining]第1輪迴調Action,值:2
[第一次遍歷forEachRemaining]第2輪迴調Action,值:1
[第一次遍歷forEachRemaining]第3輪迴調Action,值:3
對於ArrayListSpliterator
的實現可以確認下面幾點:
- 一個新的
ArrayListSpliterator
例項中的forEachRemaining()
方法只能呼叫一次 ArrayListSpliterator
例項中的forEachRemaining()
方法遍歷元素的邊界是[index, fence)
ArrayListSpliterator
自分割的時候,分割出來的新ArrayListSpliterator
負責處理元素下標小的分段(類比fork
的左分支),而原ArrayListSpliterator
負責處理元素下標大的分段(類比fork
的右分支)ArrayListSpliterator
提供的estimateSize()
方法得到的分段元素剩餘數量是一個準確值
如果把上面的例子繼續分割,可以得到下面的過程:
Spliterator
自分割是並行流實現的基礎,並行流計算過程其實就是fork-join
的處理過程,trySplit()
方法的實現決定了fork
任務的粒度,每個fork
任務進行計算的時候是併發安全的,這一點由執行緒封閉(執行緒棧封閉)保證,每一個fork
任務計算完成最後的結果再由單個執行緒進行join
操作,才能得到正確的結果。下面的例子是求整數1 ~ 100
的和:
public class ConcurrentSplitCalculateSum {
private static class ForkTask extends Thread {
private int result = 0;
private final Spliterator<Integer> spliterator;
private final CountDownLatch latch;
public ForkTask(Spliterator<Integer> spliterator,
CountDownLatch latch) {
this.spliterator = spliterator;
this.latch = latch;
}
@Override
public void run() {
long start = System.currentTimeMillis();
spliterator.forEachRemaining(num -> result = result + num);
long end = System.currentTimeMillis();
System.out.printf("執行緒[%s]完成計算任務,當前段計算結果:%d,耗時:%d ms\n",
Thread.currentThread().getName(), result, end - start);
latch.countDown();
}
public int result() {
return result;
}
}
private static int join(List<ForkTask> tasks) {
int result = 0;
for (ForkTask task : tasks) {
result = result + task.result();
}
return result;
}
private static final int THREAD_NUM = 4;
public static void main(String[] args) throws Exception {
List<Integer> source = new ArrayList<>();
for (int i = 1; i < 101; i++) {
source.add(i);
}
Spliterator<Integer> root = source.stream().spliterator();
List<Spliterator<Integer>> spliteratorList = new ArrayList<>();
Spliterator<Integer> x = root.trySplit();
Spliterator<Integer> y = x.trySplit();
Spliterator<Integer> z = root.trySplit();
spliteratorList.add(root);
spliteratorList.add(x);
spliteratorList.add(y);
spliteratorList.add(z);
List<ForkTask> tasks = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(THREAD_NUM);
for (int i = 0; i < THREAD_NUM; i++) {
ForkTask task = new ForkTask(spliteratorList.get(i), latch);
task.setName("fork-task-" + (i + 1));
tasks.add(task);
}
tasks.forEach(Thread::start);
latch.await();
int result = join(tasks);
System.out.println("最終計算結果為:" + result);
}
}
// 控制檯輸出結果
執行緒[fork-task-4]完成計算任務,當前段計算結果:1575,耗時:0 ms
執行緒[fork-task-2]完成計算任務,當前段計算結果:950,耗時:1 ms
執行緒[fork-task-3]完成計算任務,當前段計算結果:325,耗時:1 ms
執行緒[fork-task-1]完成計算任務,當前段計算結果:2200,耗時:1 ms
最終計算結果為:5050
當然,最終並行流的計算用到了ForkJoinPool
,並不像這個例子中這麼粗暴地進行非同步執行。關於並行流的實現下文會詳細分析。
Spliterator支援的特性
某一個Spliterator
例項支援的特性由方法characteristics()
決定,這個方法返回的是一個32
位數值,實際使用中會展開為bit
陣列,所有的特性分配在不同的位上,而hasCharacteristics(int characteristics)
就是通過輸入的具體特性值通過位運算判斷該特性是否存在於characteristics()
中。下面簡化characteristics
為byte
分析一下這個技巧:
假設:byte characteristics() => 也就是最多8個位用於表示特性集合,如果每個位只表示一種特性,那麼可以總共表示8種特性
特性X:0000 0001
特性Y:0000 0010
以此類推
假設:characteristics = X | Y = 0000 0001 | 0000 0010 = 0000 0011
那麼:characteristics & X = 0000 0011 & 0000 0001 = 0000 0001
判斷characteristics是否包含X:(characteristics & X) == X
上面推斷的過程就是Spliterator
中特性判斷方法的處理邏輯:
// 返回特性集合
int characteristics();
// 基於位運算判斷特性集合中是否存在輸入的特性
default boolean hasCharacteristics(int characteristics) {
return (characteristics() & characteristics) == characteristics;
}
這裡可以驗證一下:
public class CharacteristicsCheck {
public static void main(String[] args) {
System.out.printf("是否存在ORDERED特性:%s\n", hasCharacteristics(Spliterator.ORDERED));
System.out.printf("是否存在SIZED特性:%s\n", hasCharacteristics(Spliterator.SIZED));
System.out.printf("是否存在DISTINCT特性:%s\n", hasCharacteristics(Spliterator.DISTINCT));
}
private static int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED;
}
private static boolean hasCharacteristics(int characteristics) {
return (characteristics() & characteristics) == characteristics;
}
}
// 控制檯輸出
是否存在ORDERED特性:true
是否存在SIZED特性:true
是否存在DISTINCT特性:false
目前Spliterator
支援的特性一共有8
個,如下:
特性 | 十六進位制值 | 二進位制值 | 功能 |
---|---|---|---|
DISTINCT |
0x00000001 |
0000 0000 0000 0001 |
去重,例如對於每對要處理的元素(x,y) ,使用!x.equals(y) 比較,Spliterator 中去重實際上基於Set 處理 |
ORDERED |
0x00000010 |
0000 0000 0001 0000 |
(元素)順序處理,可以理解為trySplit() 、tryAdvance() 和forEachRemaining() 方法對所有元素處理都保證一個嚴格的字首順序 |
SORTED |
0x00000004 |
0000 0000 0000 0100 |
排序,元素使用getComparator() 方法提供的Comparator 進行排序,如果定義了SORTED 特性,則必須定義ORDERED 特性 |
SIZED |
0x00000040 |
0000 0000 0100 0000 |
(元素)預估數量,啟用此特性,那麼Spliterator 拆分或者迭代之前,estimateSize() 返回的是元素的準確數量 |
NONNULL |
0x00000040 |
0000 0001 0000 0000 |
(元素)非NULL ,資料來源保證Spliterator 需要處理的元素不能為NULL ,最常用於併發容器中的集合、佇列和Map |
IMMUTABLE |
0x00000400 |
0000 0100 0000 0000 |
(元素)不可變,資料來源不可被修改,也就是處理過程中元素不能被新增、替換和移除(更新屬性是允許的) |
CONCURRENT |
0x00001000 |
0001 0000 0000 0000 |
(元素源)的修改是併發安全的,意味著多執行緒在資料來源中新增、替換或者移除元素在不需要額外的同步條件下是併發安全的 |
SUBSIZED |
0x00004000 |
0100 0000 0000 0000 |
(子Spliterator 元素)預估數量,啟用此特性,意味著通過trySplit() 方法分割出來的所有子Spliterator (當前Spliterator 分割後也屬於子Spliterator )都啟用SIZED 特性 |
細心點觀察可以發現:所有特性採用32位的整數儲存,使用了隔1位儲存的策略,位下標和特性的對映是:(0 => DISTINCT)、(3 => SORTED)、(5 => ORDERED)、(7=> SIZED)、(9 => NONNULL)、(11 => IMMUTABLE)、(13 => CONCURRENT)、(15 => SUBSIZED)
所有特性的功能這裡只概括了核心的定義,還有一些小字或者特例描述限於篇幅沒有完全加上,這一點可以參考具體的原始碼中的API
註釋。這些特性最終會轉化為StreamOpFlag
再提供給Stream
中的操作判斷使用,由於StreamOpFlag
會更加複雜,下文再進行詳細分析。
流的實現原理以及原始碼分析
由於流的實現是高度抽象的工程程式碼,所以在原始碼閱讀上會有點困難。整個體系涉及到大量的介面、類和列舉,如下圖:
圖中的頂層類結構圖描述的就是流的流水線相關類繼承關係,其中IntStream
、LongStream
和DoubleStream
都是特化型別,分別針對於Integer
、Long
和Double
三種型別,其他引用型別構建的Pipeline
都是ReferencePipeline
例項,因此筆者認為,ReferencePipeline
(引用型別流水線)是流的核心資料結構,下面會基於ReferencePipeline
的實現做深入分析。
StreamOpFlag原始碼分析
注意,這一小節很燒腦,也有可能是筆者的位操作不怎麼熟練,這篇文章大部分時間消耗在這一小節
StreamOpFlag
是一個列舉,功能是儲存Stream
和操作的標誌(Flags corresponding to characteristics of streams and operations
,下稱Stream
標誌),這些標誌提供給Stream
框架用於控制、定製化和優化計算。Stream
標誌可以用於描述與流相關聯的若干不同實體的特徵,這些實體包括:Stream
的源、Stream
的中間操作(Op
)和Stream
的終端操作(Terminal Op
)。但是並非所有的Stream
標誌對所有的Stream
實體都具備意義,目前這些實體和標誌對映關係如下:
Type(Stream Entity Type) | DISTINCT | SORTED | ORDERED | SIZED | SHORT_CIRCUIT |
---|---|---|---|---|---|
SPLITERATOR |
01 | 01 | 01 | 01 | 00 |
STREAM |
01 | 01 | 01 | 01 | 00 |
OP |
11 | 11 | 11 | 10 | 01 |
TERMINAL_OP |
00 | 00 | 10 | 00 | 01 |
UPSTREAM_TERMINAL_OP |
00 | 00 | 10 | 00 | 00 |
其中:
- 01:表示設定/注入
- 10:表示清除
- 11:表示保留
- 00:表示初始化值(預設填充值),這是一個關鍵點,
0
值表示絕對不會是某個型別的標誌
StreamOpFlag
的頂部註釋中還有一個表格如下:
- | DISTINCT | SORTED | ORDERED | SIZED | SHORT_CIRCUIT |
---|---|---|---|---|---|
Stream source(Stream 的源) |
Y | Y | Y | Y | N |
Intermediate operation(中間操作) | PCI | PCI | PCI | PC | PI |
Terminal operation(終結操作) | N | N | PC | N | PI |
標記 ->
含義:
Y
:允許N
:非法P
:保留C
:清除I
:注入- 組合
PCI
:可以保留、清除或者注入 - 組合
PC
:可以保留或者清除 - 組合
PI
:可以保留或者注入
兩個表格其實是在描述同一個結論,可以相互對照和理解,但是最終實現參照於第一個表的定義。注意一點:這裡的preserved
(P
)表示保留的意思,如果Stream
實體某個標誌被賦值為preserved
,意味著該實體可以使用此標誌代表的特性。例如此小節第一個表格中的OP
的DISTINCT
、SORTED
和ORDERED
都賦值為11
(preserved
),意味著OP
型別的實體允許使用去重、自然排序和順序處理特性。回到原始碼部分,先看StreamOpFlag
的核心屬性和構造器:
enum StreamOpFlag {
// 暫時忽略其他程式碼
// 型別列舉,Stream相關實體型別
enum Type {
// SPLITERATOR型別,關聯所有和Spliterator相關的特性
SPLITERATOR,
// STREAM型別,關聯所有和Stream相關的標誌
STREAM,
// STREAM型別,關聯所有和Stream中間操作相關的標誌
OP,
// TERMINAL_OP型別,關聯所有和Stream終結操作相關的標誌
TERMINAL_OP,
// UPSTREAM_TERMINAL_OP型別,關聯所有在最後一個有狀態操作邊界上游傳播的終止操作標誌
// 這個型別的意義直譯有點拗口,不過實際上在JDK11原始碼中,這個型別沒有被流相關功能引用,暫時可以忽略
UPSTREAM_TERMINAL_OP
}
// 設定/注入標誌的bit模式,二進位制數0001,十進位制數1
private static final int SET_BITS = 0b01;
// 清除標誌的bit模式,二進位制數0010,十進位制數2
private static final int CLEAR_BITS = 0b10;
// 保留標誌的bit模式,二進位制數0011,十進位制數3
private static final int PRESERVE_BITS = 0b11;
// 掩碼建造器工廠方法,注意這個方法用於例項化MaskBuilder
private static MaskBuilder set(Type t) {
return new MaskBuilder(new EnumMap<>(Type.class)).set(t);
}
// 私有靜態內部類,掩碼建造器,裡面的map由上面的set(Type t)方法得知是EnumMap例項
private static class MaskBuilder {
// Type -> SET_BITS|CLEAR_BITS|PRESERVE_BITS|0
final Map<Type, Integer> map;
MaskBuilder(Map<Type, Integer> map) {
this.map = map;
}
// 設定型別和對應的掩碼
MaskBuilder mask(Type t, Integer i) {
map.put(t, i);
return this;
}
// 對型別新增/inject
MaskBuilder set(Type t) {
return mask(t, SET_BITS);
}
MaskBuilder clear(Type t) {
return mask(t, CLEAR_BITS);
}
MaskBuilder setAndClear(Type t) {
return mask(t, PRESERVE_BITS);
}
// 這裡的build方法對於型別中的NULL掩碼填充為0,然後把map返回
Map<Type, Integer> build() {
for (Type t : Type.values()) {
map.putIfAbsent(t, 0b00);
}
return map;
}
}
// 型別->掩碼對映
private final Map<Type, Integer> maskTable;
// bit的起始偏移量,控制下面set、clear和preserve的起始偏移量
private final int bitPosition;
// set/inject的bit set(map),其實準確來說應該是一個表示set/inject的bit map
private final int set;
// clear的bit set(map),其實準確來說應該是一個表示clear的bit map
private final int clear;
// preserve的bit set(map),其實準確來說應該是一個表示preserve的bit map
private final int preserve;
private StreamOpFlag(int position, MaskBuilder maskBuilder) {
// 這裡會基於MaskBuilder初始化內部的EnumMap
this.maskTable = maskBuilder.build();
// Two bits per flag <= 這裡會把入參position放大一倍
position *= 2;
this.bitPosition = position;
this.set = SET_BITS << position; // 設定/注入標誌的bit模式左移2倍position
this.clear = CLEAR_BITS << position; // 清除標誌的bit模式左移2倍position
this.preserve = PRESERVE_BITS << position; // 保留標誌的bit模式左移2倍position
}
// 省略中間一些方法
// 下面這些靜態變數就是直接返回標誌對應的set/injec、清除和保留的bit map
/**
* The bit value to set or inject {@link #DISTINCT}.
*/
static final int IS_DISTINCT = DISTINCT.set;
/**
* The bit value to clear {@link #DISTINCT}.
*/
static final int NOT_DISTINCT = DISTINCT.clear;
/**
* The bit value to set or inject {@link #SORTED}.
*/
static final int IS_SORTED = SORTED.set;
/**
* The bit value to clear {@link #SORTED}.
*/
static final int NOT_SORTED = SORTED.clear;
/**
* The bit value to set or inject {@link #ORDERED}.
*/
static final int IS_ORDERED = ORDERED.set;
/**
* The bit value to clear {@link #ORDERED}.
*/
static final int NOT_ORDERED = ORDERED.clear;
/**
* The bit value to set {@link #SIZED}.
*/
static final int IS_SIZED = SIZED.set;
/**
* The bit value to clear {@link #SIZED}.
*/
static final int NOT_SIZED = SIZED.clear;
/**
* The bit value to inject {@link #SHORT_CIRCUIT}.
*/
static final int IS_SHORT_CIRCUIT = SHORT_CIRCUIT.set;
}
又因為StreamOpFlag
是一個列舉,一個列舉成員是一個獨立的標誌,而一個標誌會對多個Stream
實體型別產生作用,所以它的一個成員描述的是上面實體和標誌對映關係的一個列(豎著看):
// 縱向看
DISTINCT Flag:
maskTable: {
SPLITERATOR: 0000 0001,
STREAM: 0000 0001,
OP: 0000 0011,
TERMINAL_OP: 0000 0000,
UPSTREAM_TERMINAL_OP: 0000 0000
}
position(input): 0
bitPosition: 0
set: 1 => 0000 0000 0000 0000 0000 0000 0000 0001
clear: 2 => 0000 0000 0000 0000 0000 0000 0000 0010
preserve: 3 => 0000 0000 0000 0000 0000 0000 0000 0011
SORTED Flag:
maskTable: {
SPLITERATOR: 0000 0001,
STREAM: 0000 0001,
OP: 0000 0011,
TERMINAL_OP: 0000 0000,
UPSTREAM_TERMINAL_OP: 0000 0000
}
position(input): 1
bitPosition: 2
set: 4 => 0000 0000 0000 0000 0000 0000 0000 0100
clear: 8 => 0000 0000 0000 0000 0000 0000 0000 1000
preserve: 12 => 0000 0000 0000 0000 0000 0000 0000 1100
ORDERED Flag:
maskTable: {
SPLITERATOR: 0000 0001,
STREAM: 0000 0001,
OP: 0000 0011,
TERMINAL_OP: 0000 0010,
UPSTREAM_TERMINAL_OP: 0000 0010
}
position(input): 2
bitPosition: 4
set: 16 => 0000 0000 0000 0000 0000 0000 0001 0000
clear: 32 => 0000 0000 0000 0000 0000 0000 0010 0000
preserve: 48 => 0000 0000 0000 0000 0000 0000 0011 0000
SIZED Flag:
maskTable: {
SPLITERATOR: 0000 0001,
STREAM: 0000 0001,
OP: 0000 0010,
TERMINAL_OP: 0000 0000,
UPSTREAM_TERMINAL_OP: 0000 0000
}
position(input): 3
bitPosition: 6
set: 64 => 0000 0000 0000 0000 0000 0000 0100 0000
clear: 128 => 0000 0000 0000 0000 0000 0000 1000 0000
preserve: 192 => 0000 0000 0000 0000 0000 0000 1100 0000
SHORT_CIRCUIT Flag:
maskTable: {
SPLITERATOR: 0000 0000,
STREAM: 0000 0000,
OP: 0000 0001,
TERMINAL_OP: 0000 0001,
UPSTREAM_TERMINAL_OP: 0000 0000
}
position(input): 12
bitPosition: 24
set: 16777216 => 0000 0001 0000 0000 0000 0000 0000 0000
clear: 33554432 => 0000 0010 0000 0000 0000 0000 0000 0000
preserve: 50331648 => 0000 0011 0000 0000 0000 0000 0000 0000
接著就用到按位與(&
)和按位或(|
)的操作,假設A = 0001
、B = 0010
、C = 1000
,那麼:
A|B = A | B = 0001 | 0010 = 0011
(按位或,1|0=1, 0|1=1,0|0 =0,1|1=1
)A&B = A & B = 0001 | 0010 = 0000
(按位與,1|0=0, 0|1=0,0|0 =0,1|1=1
)MASK = A | B | C = 0001 | 0010 | 1000 = 1011
- 那麼判斷
A|B
是否包含A
的條件為:A == (A|B & A)
- 那麼判斷
MASK
是否包含A
的條件為:A == MASK & A
這裡把StreamOpFlag
中的列舉套用進去分析:
static int DISTINCT_SET = 0b0001;
static int SORTED_CLEAR = 0b1000;
public static void main(String[] args) throws Exception {
// 支援DISTINCT標誌和不支援SORTED標誌
int flags = DISTINCT_SET | SORTED_CLEAR;
System.out.println(Integer.toBinaryString(flags));
System.out.printf("支援DISTINCT標誌:%s\n", DISTINCT_SET == (DISTINCT_SET & flags));
System.out.printf("不支援SORTED標誌:%s\n", SORTED_CLEAR == (SORTED_CLEAR & flags));
}
// 控制檯輸出
1001
支援DISTINCT標誌:true
不支援SORTED標誌:true
由於StreamOpFlag
的修飾符是預設,不能直接使用,可以把它的程式碼拷貝出來修改包名驗證裡面的功能:
public static void main(String[] args) {
int flags = StreamOpFlag.DISTINCT.set | StreamOpFlag.SORTED.clear;
System.out.println(StreamOpFlag.DISTINCT.set == (StreamOpFlag.DISTINCT.set & flags));
System.out.println(StreamOpFlag.SORTED.clear == (StreamOpFlag.SORTED.clear & flags));
}
// 輸出
true
true
下面這些方法就是基於這些運算特性而定義的:
enum StreamOpFlag {
// 暫時忽略其他程式碼
// 返回當前StreamOpFlag的set/inject的bit map
int set() {
return set;
}
// 返回當前StreamOpFlag的清除的bit map
int clear() {
return clear;
}
// 這裡判斷當前StreamOpFlag型別->標記對映中Stream型別的標記,如果大於0說明不是初始化狀態,那麼當前StreamOpFlag就是Stream相關的標誌
boolean isStreamFlag() {
return maskTable.get(Type.STREAM) > 0;
}
// 這裡就用到按位與判斷輸入的flags中是否設定當前StreamOpFlag(StreamOpFlag.set)
boolean isKnown(int flags) {
return (flags & preserve) == set;
}
// 這裡就用到按位與判斷輸入的flags中是否清除當前StreamOpFlag(StreamOpFlag.clear)
boolean isCleared(int flags) {
return (flags & preserve) == clear;
}
// 這裡就用到按位與判斷輸入的flags中是否保留當前StreamOpFlag(StreamOpFlag.clear)
boolean isPreserved(int flags) {
return (flags & preserve) == preserve;
}
// 判斷當前的Stream實體型別是否可以設定本標誌,要求Stream實體型別的標誌位為set或者preserve,按位與要大於0
boolean canSet(Type t) {
return (maskTable.get(t) & SET_BITS) > 0;
}
// 暫時忽略其他程式碼
}
這裡有個特殊操作,位運算的時候採用了(flags & preserve)
,理由是:同一個標誌中的同一個Stream
實體型別只可能存在set/inject
、clear
和preserve
的其中一種,也就是同一個flags
中不可能同時存在StreamOpFlag.SORTED.set
和StreamOpFlag.SORTED.clear
,從語義上已經矛盾,而set/inject
、clear
和preserve
在bit map
中的大小(為2
位)和位置已經是固定的,preserve
在設計的時候為0b11
剛好2
位取反,因此可以特化為(這個特化也讓判斷更加嚴謹):
(flags & set) == set => (flags & preserve) == set
(flags & clear) == clear => (flags & preserve) == clear
(flags & preserve) == preserve => (flags & preserve) == preserve
分析這麼多,總的來說,就是想通過一個32
位整數,每2
位分別表示3
種狀態,那麼一個完整的Flags
(標誌集合)一共可以表示16
種標誌(position=[0,15]
,可以檢視API
註釋,[4,11]
和[13,15]
的位置是未需實現或者預留的,屬於gap
)。接著分析掩碼Mask
的計算過程例子:
// 橫向看(位移動運算子優先順序高於與或,例如<<的優先順序比|高)
SPLITERATOR_CHARACTERISTICS_MASK:
mask(init) = 0
mask(DISTINCT,SPLITERATOR[DISTINCT]=01,bitPosition=0) = 0000 0000 | 0000 0001 << 0 = 0000 0000 | 0000 0001 = 0000 0001
mask(SORTED,SPLITERATOR[SORTED]=01,bitPosition=2) = 0000 0001 | 0000 0001 << 2 = 0000 0001 | 0000 0100 = 0000 0101
mask(ORDERED,SPLITERATOR[ORDERED]=01,bitPosition=4) = 0000 0101 | 0000 0001 << 4 = 0000 0101 | 0001 0000 = 0001 0101
mask(SIZED,SPLITERATOR[SIZED]=01,bitPosition=6) = 0001 0101 | 0000 0001 << 6 = 0001 0101 | 0100 0000 = 0101 0101
mask(SHORT_CIRCUIT,SPLITERATOR[SHORT_CIRCUIT]=00,bitPosition=24) = 0101 0101 | 0000 0000 << 24 = 0101 0101 | 0000 0000 = 0101 0101
mask(final) = 0000 0000 0000 0000 0000 0000 0101 0101(二進位制)、85(十進位制)
STREAM_MASK:
mask(init) = 0
mask(DISTINCT,SPLITERATOR[DISTINCT]=01,bitPosition=0) = 0000 0000 | 0000 0001 << 0 = 0000 0000 | 0000 0001 = 0000 0001
mask(SORTED,SPLITERATOR[SORTED]=01,bitPosition=2) = 0000 0001 | 0000 0001 << 2 = 0000 0001 | 0000 0100 = 0000 0101
mask(ORDERED,SPLITERATOR[ORDERED]=01,bitPosition=4) = 0000 0101 | 0000 0001 << 4 = 0000 0101 | 0001 0000 = 0001 0101
mask(SIZED,SPLITERATOR[SIZED]=01,bitPosition=6) = 0001 0101 | 0000 0001 << 6 = 0001 0101 | 0100 0000 = 0101 0101
mask(SHORT_CIRCUIT,SPLITERATOR[SHORT_CIRCUIT]=00,bitPosition=24) = 0101 0101 | 0000 0000 << 24 = 0101 0101 | 0000 0000 = 0101 0101
mask(final) = 0000 0000 0000 0000 0000 0000 0101 0101(二進位制)、85(十進位制)
OP_MASK:
mask(init) = 0
mask(DISTINCT,SPLITERATOR[DISTINCT]=11,bitPosition=0) = 0000 0000 | 0000 0011 << 0 = 0000 0000 | 0000 0011 = 0000 0011
mask(SORTED,SPLITERATOR[SORTED]=11,bitPosition=2) = 0000 0011 | 0000 0011 << 2 = 0000 0011 | 0000 1100 = 0000 1111
mask(ORDERED,SPLITERATOR[ORDERED]=11,bitPosition=4) = 0000 1111 | 0000 0011 << 4 = 0000 1111 | 0011 0000 = 0011 1111
mask(SIZED,SPLITERATOR[SIZED]=10,bitPosition=6) = 0011 1111 | 0000 0010 << 6 = 0011 1111 | 1000 0000 = 1011 1111
mask(SHORT_CIRCUIT,SPLITERATOR[SHORT_CIRCUIT]=01,bitPosition=24) = 1011 1111 | 0000 0001 << 24 = 1011 1111 | 0100 0000 0000 0000 0000 0000 0000 = 0100 0000 0000 0000 0000 1011 1111
mask(final) = 0000 0000 1000 0000 0000 0000 1011 1111(二進位制)、16777407(十進位制)
TERMINAL_OP_MASK:
mask(init) = 0
mask(DISTINCT,SPLITERATOR[DISTINCT]=00,bitPosition=0) = 0000 0000 | 0000 0000 << 0 = 0000 0000 | 0000 0000 = 0000 0000
mask(SORTED,SPLITERATOR[SORTED]=00,bitPosition=2) = 0000 0000 | 0000 0000 << 2 = 0000 0000 | 0000 0000 = 0000 0000
mask(ORDERED,SPLITERATOR[ORDERED]=10,bitPosition=4) = 0000 0000 | 0000 0010 << 4 = 0000 0000 | 0010 0000 = 0010 0000
mask(SIZED,SPLITERATOR[SIZED]=00,bitPosition=6) = 0010 0000 | 0000 0000 << 6 = 0010 0000 | 0000 0000 = 0010 0000
mask(SHORT_CIRCUIT,SPLITERATOR[SHORT_CIRCUIT]=01,bitPosition=24) = 0010 0000 | 0000 0001 << 24 = 0010 0000 | 0001 0000 0000 0000 0000 0000 0000 = 0001 0000 0000 0000 0000 0010 0000
mask(final) = 0000 0001 0000 0000 0000 0000 0010 0000(二進位制)、16777248(十進位制)
UPSTREAM_TERMINAL_OP_MASK:
mask(init) = 0
mask(DISTINCT,SPLITERATOR[DISTINCT]=00,bitPosition=0) = 0000 0000 | 0000 0000 << 0 = 0000 0000 | 0000 0000 = 0000 0000
mask(SORTED,SPLITERATOR[SORTED]=00,bitPosition=2) = 0000 0000 | 0000 0000 << 2 = 0000 0000 | 0000 0000 = 0000 0000
mask(ORDERED,SPLITERATOR[ORDERED]=10,bitPosition=4) = 0000 0000 | 0000 0010 << 4 = 0000 0000 | 0010 0000 = 0010 0000
mask(SIZED,SPLITERATOR[SIZED]=00,bitPosition=6) = 0010 0000 | 0000 0000 << 6 = 0010 0000 | 0000 0000 = 0010 0000
mask(SHORT_CIRCUIT,SPLITERATOR[SHORT_CIRCUIT]=00,bitPosition=24) = 0010 0000 | 0000 0000 << 24 = 0010 0000 | 0000 0000 = 0010 0000
mask(final) = 0000 0000 0000 0000 0000 0000 0010 0000(二進位制)、32(十進位制)
相關的方法和屬性如下:
enum StreamOpFlag {
// SPLITERATOR型別的標誌bit map
static final int SPLITERATOR_CHARACTERISTICS_MASK = createMask(Type.SPLITERATOR);
// STREAM型別的標誌bit map
static final int STREAM_MASK = createMask(Type.STREAM);
// OP型別的標誌bit map
static final int OP_MASK = createMask(Type.OP);
// TERMINAL_OP型別的標誌bit map
static final int TERMINAL_OP_MASK = createMask(Type.TERMINAL_OP);
// UPSTREAM_TERMINAL_OP型別的標誌bit map
static final int UPSTREAM_TERMINAL_OP_MASK = createMask(Type.UPSTREAM_TERMINAL_OP);
// 基於Stream型別,建立對應型別填充所有標誌的bit map
private static int createMask(Type t) {
int mask = 0;
for (StreamOpFlag flag : StreamOpFlag.values()) {
mask |= flag.maskTable.get(t) << flag.bitPosition;
}
return mask;
}
// 構造一個標誌本身的掩碼,就是所有標誌都採用保留位表示,目前作為flags == 0時候的初始值
private static final int FLAG_MASK = createFlagMask();
// 構造一個包含全部標誌中的preserve位的bit map,按照目前來看是暫時是一個固定值,二進位制表示為0011 0000 0000 0000 0000 1111 1111
private static int createFlagMask() {
int mask = 0;
for (StreamOpFlag flag : StreamOpFlag.values()) {
mask |= flag.preserve;
}
return mask;
}
// 構造一個Stream型別包含全部標誌中的set位的bit map,這裡直接使用了STREAM_MASK,按照目前來看是暫時是一個固定值,二進位制表示為0000 0000 0000 0000 0000 0000 0101 0101
private static final int FLAG_MASK_IS = STREAM_MASK;
// 構造一個Stream型別包含全部標誌中的clear位的bit map,按照目前來看是暫時是一個固定值,二進位制表示為0000 0000 0000 0000 0000 0000 1010 1010
private static final int FLAG_MASK_NOT = STREAM_MASK << 1;
// 初始化操作的標誌bit map,目前來看就是Stream的頭節點初始化時候需要合併在flags裡面的初始化值,照目前來看是暫時是一個固定值,二進位制表示為0000 0000 0000 0000 0000 0000 1111 1111
static final int INITIAL_OPS_VALUE = FLAG_MASK_IS | FLAG_MASK_NOT;
}
SPLITERATOR_CHARACTERISTICS_MASK
等5
個成員(見上面的Mask
計算例子)其實就是預先計算好對應的Stream
實體型別的所有StreamOpFlag
標誌的bit map
,也就是之前那個展示Stream
的型別和標誌的對映圖的"橫向"展示:
前面的分析已經相對詳細,過程非常複雜,但是更復雜的Mask
應用還在後面的方法。Mask
的初始化就是提供給標誌的合併(combine
)和轉化(從Spliterator
中的characteristics
轉化為flags
)操作的,見下面的方法:
enum StreamOpFlag {
// 這個方法完全沒有註釋,只使用在下面的combineOpFlags()方法中
// 從原始碼來看
// 入參flags == 0的時候,那麼直接返回0011 0000 0000 0000 0000 1111 1111
// 入參flags != 0的時候,那麼會把當前flags的所有set/inject、clear和preserve所在位在bit map中全部置為0,然後其他位全部置為1
private static int getMask(int flags) {
return (flags == 0)
? FLAG_MASK
: ~(flags | ((FLAG_MASK_IS & flags) << 1) | ((FLAG_MASK_NOT & flags) >> 1));
}
// 合併新的flags和前一個flags,這裡還是用到老套路先和Mask按位與,再進行一次按位或
// 作為Stream的頭節點的時候,prevCombOpFlags必須為INITIAL_OPS_VALUE
static int combineOpFlags(int newStreamOrOpFlags, int prevCombOpFlags) {
// 0x01 or 0x10 nibbles are transformed to 0x11
// 0x00 nibbles remain unchanged
// Then all the bits are flipped
// Then the result is logically or'ed with the operation flags.
return (prevCombOpFlags & StreamOpFlag.getMask(newStreamOrOpFlags)) | newStreamOrOpFlags;
}
// 通過合併後的flags,轉換出Stream型別的flags
static int toStreamFlags(int combOpFlags) {
// By flipping the nibbles 0x11 become 0x00 and 0x01 become 0x10
// Shift left 1 to restore set flags and mask off anything other than the set flags
return ((~combOpFlags) >> 1) & FLAG_MASK_IS & combOpFlags;
}
// Stream的標誌轉換為Spliterator的characteristics
static int toCharacteristics(int streamFlags) {
return streamFlags & SPLITERATOR_CHARACTERISTICS_MASK;
}
// Spliterator的characteristics轉換為Stream的標誌,入參是Spliterator例項
static int fromCharacteristics(Spliterator<?> spliterator) {
int characteristics = spliterator.characteristics();
if ((characteristics & Spliterator.SORTED) != 0 && spliterator.getComparator() != null) {
// Do not propagate the SORTED characteristic if it does not correspond
// to a natural sort order
return characteristics & SPLITERATOR_CHARACTERISTICS_MASK & ~Spliterator.SORTED;
}
else {
return characteristics & SPLITERATOR_CHARACTERISTICS_MASK;
}
}
// Spliterator的characteristics轉換為Stream的標誌,入參是Spliterator的characteristics
static int fromCharacteristics(int characteristics) {
return characteristics & SPLITERATOR_CHARACTERISTICS_MASK;
}
}
這裡的位運算很複雜,只展示簡單的計算結果和相關功能:
combineOpFlags()
:用於合併新的flags
和上一個flags
,因為Stream
的資料結構是一個Pipeline
,後繼節點需要合併前驅節點的flags
,例如前驅節點flags
是ORDERED.set
,當前新加入Pipeline
的節點(後繼節點)的新flags
為SIZED.set
,那麼在後繼節點中應該合併前驅節點的標誌,簡單想象為SIZED.set | ORDERED.set
,如果是頭節點,那麼初始化頭節點時候的flags
要合併INITIAL_OPS_VALUE
,這裡舉個例子:
int left = ORDERED.set | DISTINCT.set;
int right = SIZED.clear | SORTED.clear;
System.out.println("left:" + Integer.toBinaryString(left));
System.out.println("right:" + Integer.toBinaryString(right));
System.out.println("right mask:" + Integer.toBinaryString(getMask(right)));
System.out.println("combine:" + Integer.toBinaryString(combineOpFlags(right, left)));
// 輸出結果
left:1010001
right:10001000
right mask:11111111111111111111111100110011
combine:10011001
characteristics
的轉化問題:Spliterator
中的characteristics
可以通過簡單的按位與轉換為flags
的原因是Spliterator
中的characteristics
在設計時候本身就是和StreamOpFlag
匹配的,準確來說就是bit map
的位分佈是匹配的,所以直接與SPLITERATOR_CHARACTERISTICS_MASK
做按位與即可,見下面的例子:
// 這裡簡單點只展示8 bit
SPLITERATOR_CHARACTERISTICS_MASK: 0101 0101
Spliterator.ORDERED: 0001 0000
StreamOpFlag.ORDERED.set: 0001 0000
至此,已經分析完StreamOpFlag
的完整實現,Mask
相關的方法限於篇幅就不打算詳細展開,下面會開始分析Stream
中的"流水線"結構實現,因為習慣問題,下文的"標誌"和"特性"兩個詞語會混用。
ReferencePipeline原始碼分析
既然Stream
具備流的特性,那麼就需要一個鏈式資料結構,讓元素能夠從Source
一直往下"流動"和傳遞到每一個鏈節點,實現這種場景的常用資料結構就是雙向連結串列(考慮需要回溯,單向連結串列不太合適),目前比較著名的實現有AQS
和Netty
中的ChannelHandlerContext
。例如Netty
中的流水線ChannelPipeline
設計如下:
對於這個雙向連結串列的資料結構,Stream
中對應的類就是AbstractPipeline
,核心實現類在ReferencePipeline
和ReferencePipeline
的內部類。
主要介面
先簡單展示AbstractPipeline
的核心父類方法定義,主要接父類是Stream
、BaseStream
和PipelineHelper
:
Stream
代表一個支援序列和並行聚合操作集合的元素序列,此頂層介面提供了流中間操作、終結操作和一些靜態工廠方法的定義(由於方法太多,這裡不全部列舉),這個介面本質是一個建造器型別介面(對接中間操作來說),可以構成一個多中間操作,單終結操作的鏈,例如:
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// 忽略其他程式碼
// 過濾Op
Stream<T> filter(Predicate<? super T> predicate);
// 對映Op
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
// 終結操作 - 遍歷
void forEach(Consumer<? super T> action);
// 忽略其他程式碼
}
// init
Stream x = buildStream();
// chain: head -> filter(Op) -> map(Op) -> forEach(Terminal Op)
x.filter().map().forEach()
BaseStream
:Stream
的基礎介面,定義流的迭代器、流的等效變體(併發處理變體、同步處理變體和不支援順序處理元素變體)、併發和同步判斷以及關閉相關方法
// T是元素型別,S是BaseStream<T, S>型別
// 流的基礎介面,這裡的流指定的支援同步執行和非同步執行的聚合操作的元素序列
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
// 返回一個當前Stream例項中所有元素的迭代器
// 這是一個終結操作
Iterator<T> iterator();
// 返回一個當前Stream例項中所有元素的可拆分迭代器
Spliterator<T> spliterator();
// 當前的Stream例項是否支援併發
boolean isParallel();
// 返回一個等效的同步處理的Stream例項
S sequential();
// 返回一個等效的併發處理的Stream例項
S parallel();
// 返回一個等效的不支援StreamOpFlag.ORDERED特性的Stream例項
// 或者說支援StreamOpFlag.NOT_ORDERED的特性,也就返回的變體Stream在處理元素的時候不需要順序處理
S unordered();
// 返回一個新增了close處理器的Stream例項,close處理器會在下面的close方法中回撥
S onClose(Runnable closeHandler);
// 關閉當前Stream例項,回撥關聯本Stream的所有close處理器
@Override
void close();
}
PipelineHelper
:
abstract class PipelineHelper<P_OUT> {
// 獲取流的流水線的資料來源的"形狀",其實就是資料來源元素的型別
// 主要有四種型別:REFERENCE(除了int、long和double之外的引用型別)、INT_VALUE、LONG_VALUE和DOUBLE_VALUE
abstract StreamShape getSourceShape();
// 獲取合併流和流操作的標誌,合併的標誌包括流的資料來源標誌、中間操作標誌和終結操作標誌
// 從實現上看是當前流管道節點合併前面所有節點和自身節點標誌的所有標誌
abstract int getStreamAndOpFlags();
// 如果當前的流管道節點的合併標誌集合支援SIZED,則呼叫Spliterator.getExactSizeIfKnown()返回資料來源中的準確元素數量,否則返回-1
abstract<P_IN> long exactOutputSizeIfKnown(Spliterator<P_IN> spliterator);
// 相當於呼叫下面的方法組合:copyInto(wrapSink(sink), spliterator)
abstract<P_IN, S extends Sink<P_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator);
// 傳送所有來自Spliterator中的元素到Sink中,如果支援SHORT_CIRCUIT標誌,則會呼叫copyIntoWithCancel
abstract<P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator);
// 傳送所有來自Spliterator中的元素到Sink中,Sink處理完每個元素後會檢查Sink#cancellationRequested()方法的狀態去判斷是否中斷推送元素的操作
abstract <P_IN> boolean copyIntoWithCancel(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator);
// 建立接收元素型別為P_IN的Sink例項,實現PipelineHelper中描述的所有中間操作,用這個Sink去包裝傳入的Sink例項(傳入的Sink例項的元素型別為PipelineHelper的輸出型別P_OUT)
abstract<P_IN> Sink<P_IN> wrapSink(Sink<P_OUT> sink);
// 包裝傳入的spliterator,從原始碼來看,在Stream鏈的頭節點呼叫會直接返回傳入的例項,如果在非頭節點呼叫會委託到StreamSpliterators.WrappingSpliterator()方法進行包裝
// 這個方法在原始碼中沒有API註釋
abstract<P_IN> Spliterator<P_OUT> wrapSpliterator(Spliterator<P_IN> spliterator);
// 構造一個相容當前Stream元素"形狀"的Node.Builder例項
// 從原始碼來看直接委託到Nodes.builder()方法
abstract Node.Builder<P_OUT> makeNodeBuilder(long exactSizeIfKnown,
IntFunction<P_OUT[]> generator);
// Stream流水線所有階段(節點)應用於資料來源Spliterator,輸出的元素作為結果收集起來轉化為Node例項
// 此方法應用於toArray()方法的計算,本質上是一個終結操作
abstract<P_IN> Node<P_OUT> evaluate(Spliterator<P_IN> spliterator,
boolean flatten,
IntFunction<P_OUT[]> generator);
}
注意一點(重複3
次):
- 這裡把同步流稱為同步處理|執行的流,"並行流"稱為併發處理|執行的流,因為並行流有歧義,實際上只是併發執行,不是並行執行
- 這裡把同步流稱為同步處理|執行的流,"並行流"稱為併發處理|執行的流,因為並行流有歧義,實際上只是併發執行,不是並行執行
- 這裡把同步流稱為同步處理|執行的流,"並行流"稱為併發處理|執行的流,因為並行流有歧義,實際上只是併發執行,不是並行執行
Sink和引用型別鏈
PipelineHelper
的幾個方法中存在Sink
這個介面,上一節沒有分析,這一小節會詳細展開。Stream
在構建的時候雖然是一個雙向連結串列的結構,但是在最終應用終結操作的時候,會把所有操作轉化為引用型別鏈(ChainedReference
),記得之前也提到過這種類似於多層包裝器的程式設計模式,簡化一下模型如下:
public class WrapperApp {
interface Wrapper {
void doAction();
}
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
Wrapper first = () -> System.out.printf("wrapper [depth => %d] invoke\n", counter.incrementAndGet());
Wrapper second = () -> {
first.doAction();
System.out.printf("wrapper [depth => %d] invoke\n", counter.incrementAndGet());
};
second.doAction();
}
}
// 控制檯輸出
wrapper [depth => 1] invoke
wrapper [depth => 2] invoke
上面的例子有點突兀,兩個不同Sink
的實現可以做到無感知融合,舉另一個例子如下:
public interface Sink<T> extends Consumer<T> {
default void begin(long size) {
}
default void end() {
}
abstract class ChainedReference<T, OUT> implements Sink<T> {
protected final Sink<OUT> downstream;
public ChainedReference(Sink<OUT> downstream) {
this.downstream = downstream;
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public class ReferenceChain<OUT, R> {
/**
* sink chain
*/
private final List<Supplier<Sink<?>>> sinkBuilders = new ArrayList<>();
/**
* current sink
*/
private final AtomicReference<Sink> sinkReference = new AtomicReference<>();
public ReferenceChain<OUT, R> filter(Predicate<OUT> predicate) {
//filter
sinkBuilders.add(() -> {
Sink<OUT> prevSink = (Sink<OUT>) sinkReference.get();
Sink.ChainedReference<OUT, OUT> currentSink = new Sink.ChainedReference<>(prevSink) {
@Override
public void accept(OUT out) {
if (predicate.test(out)) {
downstream.accept(out);
}
}
};
sinkReference.set(currentSink);
return currentSink;
});
return this;
}
public ReferenceChain<OUT, R> map(Function<OUT, R> function) {
// map
sinkBuilders.add(() -> {
Sink<R> prevSink = (Sink<R>) sinkReference.get();
Sink.ChainedReference<OUT, R> currentSink = new Sink.ChainedReference<>(prevSink) {
@Override
public void accept(OUT in) {
downstream.accept(function.apply(in));
}
};
sinkReference.set(currentSink);
return currentSink;
});
return this;
}
public void forEachPrint(Collection<OUT> collection) {
forEachPrint(collection, false);
}
public void forEachPrint(Collection<OUT> collection, boolean reverse) {
Spliterator<OUT> spliterator = collection.spliterator();
// 這個是類似於terminal op
Sink<OUT> sink = System.out::println;
sinkReference.set(sink);
Sink<OUT> stage = sink;
// 反向包裝 -> 正向遍歷
if (reverse) {
for (int i = 0; i <= sinkBuilders.size() - 1; i++) {
Supplier<Sink<?>> supplier = sinkBuilders.get(i);
stage = (Sink<OUT>) supplier.get();
}
} else {
// 正向包裝 -> 反向遍歷
for (int i = sinkBuilders.size() - 1; i >= 0; i--) {
Supplier<Sink<?>> supplier = sinkBuilders.get(i);
stage = (Sink<OUT>) supplier.get();
}
}
Sink<OUT> finalStage = stage;
spliterator.forEachRemaining(finalStage);
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(12);
ReferenceChain<Integer, Integer> chain = new ReferenceChain<>();
// filter -> map -> for each
chain.filter(item -> item > 10)
.map(item -> item * 2)
.forEachPrint(list);
}
}
// 輸出結果
24
執行的流程如下:
多層包裝器的程式設計模式的核心要領就是:
- 絕大部分操作可以轉換為
java.util.function.Consumer
的實現,也就是實現accept(T t)
方法完成對傳入的元素進行處理 - 先處理的
Sink
總是以後處理的Sink
為入參,在自身處理方法中判斷和回撥傳入的Sink
的處理方法回撥,也就是構建引用鏈的時候,需要從後往前構建,這種方式的實現邏輯可以參考AbstractPipeline#wrapSink()
,例如:
// 目標順序:filter -> map
Sink mapSink = new Sink(inputSink){
private Function mapper;
public void accept(E ele) {
inputSink.accept(mapper.apply(ele))
}
}
Sink filterSink = new Sink(mapSink){
private Predicate predicate;
public void accept(E ele) {
if(predicate.test(ele)){
mapSink.accept(ele);
}
}
}
- 由上一點得知,一般來說,最後的終結操作會應用在引用鏈的第一個
Sink
上
上面的程式碼並非筆者虛構出來,可見java.util.stream.Sink
的原始碼:
// 繼承自Consumer,主要是繼承函式式介面方法void accept(T t)
interface Sink<T> extends Consumer<T> {
// 重置當前Sink的狀態(為了接收一個新的資料集),傳入的size是推送到downstream的準確資料量,無法評估資料量則傳入-1
default void begin(long size) {}
//
default void end() {}
// 返回true的時候表示當前的Sink不會接收資料
default boolean cancellationRequested() {
return false;
}
// 特化方法,接受一個int型別的值
default void accept(int value) {
throw new IllegalStateException("called wrong accept method");
}
// 特化方法,接受一個long型別的值
default void accept(long value) {
throw new IllegalStateException("called wrong accept method");
}
// 特化方法,接受一個double型別的值
default void accept(double value) {
throw new IllegalStateException("called wrong accept method");
}
// 引用型別鏈,準確來說是Sink鏈
abstract static class ChainedReference<T, E_OUT> implements Sink<T> {
// 下一個Sink
protected final Sink<? super E_OUT> downstream;
public ChainedReference(Sink<? super E_OUT> downstream) {
this.downstream = Objects.requireNonNull(downstream);
}
@Override
public void begin(long size) {
downstream.begin(size);
}
@Override
public void end() {
downstream.end();
}
@Override
public boolean cancellationRequested() {
return downstream.cancellationRequested();
}
}
// 暫時忽略Int、Long、Double的特化型別場景
}
如果用過RxJava
或者Project-Reactor
,Sink
更像是Subscriber
,多個Subscriber
組成了ChainedReference
(Sink Chain
,可以理解為一個複合的Subscriber
),而Terminal Op
則類似於Publisher
,只有在Subscriber
訂閱Publisher
的時候才會進行資料的處理,這裡是應用了Reactive
程式設計模式。
AbstractPipeline和ReferencePipeline的實現
AbstractPipeline
和ReferencePipeline
都是抽象類,AbstractPipeline
用於構建Pipeline
的資料結構,提供一些Shape
相關的抽象方法給ReferencePipeline
實現,而ReferencePipeline
就是Stream
中Pipeline
的基礎型別,從原始碼上看,Stream
鏈式(管道式)結構的頭節點和操作節點都是ReferencePipeline
的子類。先看AbstractPipeline
的成員變數和建構函式:
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
// 流管道鏈式結構的頭節點(只有當前的AbstractPipeline引用是頭節點,此變數才會被賦值,非頭節點為NULL)
@SuppressWarnings("rawtypes")
private final AbstractPipeline sourceStage;
// 流管道鏈式結構的upstream,也就是上一個節點,如果是頭節點此引用為NULL
@SuppressWarnings("rawtypes")
private final AbstractPipeline previousStage;
// 合併資料來源的標誌和操作標誌的掩碼
protected final int sourceOrOpFlags;
// 流管道鏈式結構的下一個節點,如果是頭節點此引用為NULL
@SuppressWarnings("rawtypes")
private AbstractPipeline nextStage;
// 流的深度
// 序列執行的流中,表示當前流管道例項中中間操作節點的個數(除去頭節點和終結操作)
// 併發執行的流中,表示當前流管道例項中中間操作節點和前一個有狀態操作節點之間的節點個數
private int depth;
// 合併了所有資料來源的標誌、操作標誌和當前的節點(AbstractPipeline)例項的標誌,也就是當前的節點可以基於此屬性得知所有支援的標誌
private int combinedFlags;
// 資料來源的Spliterator例項
private Spliterator<?> sourceSpliterator;
// 資料來源的Spliterator例項封裝的Supplier例項
private Supplier<? extends Spliterator<?>> sourceSupplier;
// 標記當前的流節點是否被連線或者消費掉,不能重複連線或者重複消費
private boolean linkedOrConsumed;
// 標記當前的流管道鏈式結構中是否存在有狀態的操作節點,這個屬性只會在頭節點中有效
private boolean sourceAnyStateful;
// 資料來源關閉動作,這個屬性只會在頭節點中有效,由sourceStage持有
private Runnable sourceCloseAction;
// 標記當前流是否併發執行
private boolean parallel;
// 流管道結構頭節點的父構造方法,使用資料來源的Spliterator例項封裝的Supplier例項
AbstractPipeline(Supplier<? extends Spliterator<?>> source,
int sourceFlags, boolean parallel) {
// 頭節點的前驅節點置為NULL
this.previousStage = null;
this.sourceSupplier = source;
this.sourceStage = this;
// 合併傳入的源標誌和流標誌的掩碼
this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
// The following is an optimization of:
// StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);
// 初始化合並標誌集合為sourceOrOpFlags和所有流操作標誌的初始化值
this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
// 深度設定為0
this.depth = 0;
this.parallel = parallel;
}
// 流管道結構頭節點的父構造方法,使用資料來源的Spliterator例項
AbstractPipeline(Spliterator<?> source,
int sourceFlags, boolean parallel) {
// 頭節點的前驅節點置為NULL
this.previousStage = null;
this.sourceSpliterator = source;
this.sourceStage = this;
// 合併傳入的源標誌和流標誌的掩碼
this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
// The following is an optimization of:
// StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);
// 初始化合並標誌集合為sourceOrOpFlags和所有流操作標誌的初始化值
this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
this.depth = 0;
this.parallel = parallel;
}
// 流管道結構中間操作節點的父構造方法
AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
if (previousStage.linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
previousStage.linkedOrConsumed = true;
// 設定前驅節點的後繼節點引用為當前的AbstractPipeline例項
previousStage.nextStage = this;
// 設定前驅節點引用為傳入的前驅節點例項
this.previousStage = previousStage;
// 合併傳入的中間操作標誌和流操作標誌的掩碼
this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
// 合併標誌集合為傳入的標誌和前驅節點的標誌集合
this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
// 賦值sourceStage為前驅節點的sourceStage
this.sourceStage = previousStage.sourceStage;
if (opIsStateful())
// 標記當前的流存在有狀態操作
sourceStage.sourceAnyStateful = true;
// 深度設定為前驅節點深度加1
this.depth = previousStage.depth + 1;
}
// 省略其他方法
}
至此,可以看出流管道的資料結構:
Terminal Op
不參與管道鏈式結構的構建。接著看AbstractPipeline
中的終結求值方法(Terminal evaluation methods
):
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
// 省略其他方法
// 基於終結操作進行求值,這個是Stream執行的常用核心方法,常用於collect()這類終結操作
final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
assert getOutputShape() == terminalOp.inputShape();
// 判斷linkedOrConsumed,以防多次終結求值,也就是每個終結操作只能執行一次
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
// 如果當前流支援併發執行,則委託到TerminalOp.evaluateParallel(),如果當前流只支援同步執行,則委託到TerminalOp.evaluateSequential()
// 這裡注意傳入到TerminalOp中的方法引數分別是this(PipelineHelper型別)和資料來源Spliterator
return isParallel()
? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
: terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
}
// 基於當前的流例項轉換為最終的Node例項,傳入的IntFunction用於建立陣列例項
// 此終結方法一般用於toArray()這類終結操作
@SuppressWarnings("unchecked")
final Node<E_OUT> evaluateToArrayNode(IntFunction<E_OUT[]> generator) {
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
// If the last intermediate operation is stateful then
// evaluate directly to avoid an extra collection step
// 當前流支援併發執行,並且最後一箇中間操作是有狀態,則委託到opEvaluateParallel(),否則委託到evaluate(),這兩個都是AbstractPipeline中的方法
if (isParallel() && previousStage != null && opIsStateful()) {
// Set the depth of this, last, pipeline stage to zero to slice the
// pipeline such that this operation will not be included in the
// upstream slice and upstream operations will not be included
// in this slice
depth = 0;
return opEvaluateParallel(previousStage, previousStage.sourceSpliterator(0), generator);
}
else {
return evaluate(sourceSpliterator(0), true, generator);
}
}
// 這個方法比較簡單,就是獲取當前流的資料來源所在的Spliterator,並且確保流已經消費,一般用於forEach()這類終結操作
final Spliterator<E_OUT> sourceStageSpliterator() {
if (this != sourceStage)
throw new IllegalStateException();
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
if (sourceStage.sourceSpliterator != null) {
@SuppressWarnings("unchecked")
Spliterator<E_OUT> s = sourceStage.sourceSpliterator;
sourceStage.sourceSpliterator = null;
return s;
}
else if (sourceStage.sourceSupplier != null) {
@SuppressWarnings("unchecked")
Spliterator<E_OUT> s = (Spliterator<E_OUT>) sourceStage.sourceSupplier.get();
sourceStage.sourceSupplier = null;
return s;
}
else {
throw new IllegalStateException(MSG_CONSUMED);
}
}
// 省略其他方法
}
AbstractPipeline
中實現了BaseStream
的方法:
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
// 省略其他方法
// 設定頭節點的parallel屬性為false,返回自身例項,表示當前的流是同步執行的
@Override
@SuppressWarnings("unchecked")
public final S sequential() {
sourceStage.parallel = false;
return (S) this;
}
// 設定頭節點的parallel屬性為true,返回自身例項,表示當前的流是併發執行的
@Override
@SuppressWarnings("unchecked")
public final S parallel() {
sourceStage.parallel = true;
return (S) this;
}
// 流關閉操作,設定linkedOrConsumed為true,資料來源的Spliterator相關引用置為NULL,置空並且回撥sourceCloseAction鉤子例項
@Override
public void close() {
linkedOrConsumed = true;
sourceSupplier = null;
sourceSpliterator = null;
if (sourceStage.sourceCloseAction != null) {
Runnable closeAction = sourceStage.sourceCloseAction;
sourceStage.sourceCloseAction = null;
closeAction.run();
}
}
// 返回一個新增了close處理器的Stream例項,close處理器會在下面的close方法中回撥
// 如果本來持有的引用sourceStage.sourceCloseAction非空,會使用傳入的closeHandler與sourceStage.sourceCloseAction進行合併
@Override
@SuppressWarnings("unchecked")
public S onClose(Runnable closeHandler) {
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
Objects.requireNonNull(closeHandler);
Runnable existingHandler = sourceStage.sourceCloseAction;
sourceStage.sourceCloseAction =
(existingHandler == null)
? closeHandler
: Streams.composeWithExceptions(existingHandler, closeHandler);
return (S) this;
}
// Primitive specialization use co-variant overrides, hence is not final
// 返回當前流例項中所有元素的Spliterator例項
@Override
@SuppressWarnings("unchecked")
public Spliterator<E_OUT> spliterator() {
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
// 標記當前節點被連結或者消費
linkedOrConsumed = true;
// 如果當前節點為頭節點,那麼返回sourceStage.sourceSpliterator或者延時載入的sourceStage.sourceSupplier(延時載入封裝由lazySpliterator實現)
if (this == sourceStage) {
if (sourceStage.sourceSpliterator != null) {
@SuppressWarnings("unchecked")
Spliterator<E_OUT> s = (Spliterator<E_OUT>) sourceStage.sourceSpliterator;
sourceStage.sourceSpliterator = null;
return s;
}
else if (sourceStage.sourceSupplier != null) {
@SuppressWarnings("unchecked")
Supplier<Spliterator<E_OUT>> s = (Supplier<Spliterator<E_OUT>>) sourceStage.sourceSupplier;
sourceStage.sourceSupplier = null;
return lazySpliterator(s);
}
else {
throw new IllegalStateException(MSG_CONSUMED);
}
}
else {
// 如果當前節點不是頭節點,重新對sourceSpliterator進行包裝,包裝後的例項為WrappingSpliterator
return wrap(this, () -> sourceSpliterator(0), isParallel());
}
}
// 當前流例項是否併發執行,從頭節點的parallel屬性進行判斷
@Override
public final boolean isParallel() {
return sourceStage.parallel;
}
// 從當前combinedFlags中獲取資料來源標誌和所有流中間操作標誌的集合
final int getStreamFlags() {
return StreamOpFlag.toStreamFlags(combinedFlags);
}
/**
* Get the source spliterator for this pipeline stage. For a sequential or
* stateless parallel pipeline, this is the source spliterator. For a
* stateful parallel pipeline, this is a spliterator describing the results
* of all computations up to and including the most recent stateful
* operation.
*/
@SuppressWarnings("unchecked")
private Spliterator<?> sourceSpliterator(int terminalFlags) {
// 從sourceStage.sourceSpliterator或者sourceStage.sourceSupplier中獲取當前流例項中的Spliterator例項,確保必定存在,否則丟擲IllegalStateException
Spliterator<?> spliterator = null;
if (sourceStage.sourceSpliterator != null) {
spliterator = sourceStage.sourceSpliterator;
sourceStage.sourceSpliterator = null;
}
else if (sourceStage.sourceSupplier != null) {
spliterator = (Spliterator<?>) sourceStage.sourceSupplier.get();
sourceStage.sourceSupplier = null;
}
else {
throw new IllegalStateException(MSG_CONSUMED);
}
// 下面這段邏輯是對於併發執行並且存在有狀態操作的節點,那麼需要重新計算節點的深度和節點的合併標誌集合
// 這裡只提一下計算過程,從頭節點的後繼節點開始遍歷到當前節點,如果被遍歷的節點時有狀態的,那麼對depth、combinedFlags和spliterator會進行重新計算
// depth一旦出現有狀態節點就會重置為0,然後從1重新開始增加
// combinedFlags會重新合併sourceOrOpFlags、SHORT_CIRCUIT(如果sourceOrOpFlags支援)和Spliterator.SIZED
// spliterator簡單來看就是從併發執行的toArray()=>Array陣列=>Spliterator例項
if (isParallel() && sourceStage.sourceAnyStateful) {
// Adapt the source spliterator, evaluating each stateful op
// in the pipeline up to and including this pipeline stage.
// The depth and flags of each pipeline stage are adjusted accordingly.
int depth = 1;
for (@SuppressWarnings("rawtypes") AbstractPipeline u = sourceStage, p = sourceStage.nextStage, e = this;
u != e;
u = p, p = p.nextStage) {
int thisOpFlags = p.sourceOrOpFlags;
if (p.opIsStateful()) {
depth = 0;
if (StreamOpFlag.SHORT_CIRCUIT.isKnown(thisOpFlags)) {
// Clear the short circuit flag for next pipeline stage
// This stage encapsulates short-circuiting, the next
// stage may not have any short-circuit operations, and
// if so spliterator.forEachRemaining should be used
// for traversal
thisOpFlags = thisOpFlags & ~StreamOpFlag.IS_SHORT_CIRCUIT;
}
spliterator = p.opEvaluateParallelLazy(u, spliterator);
// Inject or clear SIZED on the source pipeline stage
// based on the stage's spliterator
thisOpFlags = spliterator.hasCharacteristics(Spliterator.SIZED)
? (thisOpFlags & ~StreamOpFlag.NOT_SIZED) | StreamOpFlag.IS_SIZED
: (thisOpFlags & ~StreamOpFlag.IS_SIZED) | StreamOpFlag.NOT_SIZED;
}
p.depth = depth++;
p.combinedFlags = StreamOpFlag.combineOpFlags(thisOpFlags, u.combinedFlags);
}
}
// 如果傳入的terminalFlags標誌不為0,則當前節點的combinedFlags會合並terminalFlags
if (terminalFlags != 0) {
// Apply flags from the terminal operation to last pipeline stage
combinedFlags = StreamOpFlag.combineOpFlags(terminalFlags, combinedFlags);
}
return spliterator;
}
// 省略其他方法
}
AbstractPipeline
中實現了PipelineHelper
的方法:
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
// 省略其他方法
// 獲取資料來源元素的型別,這裡的型別包括引用、int、double和float
// 其實實現上就是獲取depth<=0的第一個節點的輸出型別
@Override
final StreamShape getSourceShape() {
@SuppressWarnings("rawtypes")
AbstractPipeline p = AbstractPipeline.this;
while (p.depth > 0) {
p = p.previousStage;
}
return p.getOutputShape();
}
// 基於當前節點的標誌集合判斷和返回流中待處理的元素數量,無法獲取則返回-1
@Override
final <P_IN> long exactOutputSizeIfKnown(Spliterator<P_IN> spliterator) {
return StreamOpFlag.SIZED.isKnown(getStreamAndOpFlags()) ? spliterator.getExactSizeIfKnown() : -1;
}
// 通過流管道鏈式結構構建元素引用鏈,再遍歷元素引用鏈
@Override
final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
return sink;
}
// 遍歷元素引用鏈
@Override
final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
Objects.requireNonNull(wrappedSink);
// 當前節點不支援SHORT_CIRCUIT(短路)特性,則直接遍歷元素引用鏈,不支援短路跳出
if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
wrappedSink.begin(spliterator.getExactSizeIfKnown());
spliterator.forEachRemaining(wrappedSink);
wrappedSink.end();
}
else {
// 支援短路(中途取消)遍歷元素引用鏈
copyIntoWithCancel(wrappedSink, spliterator);
}
}
// 支援短路(中途取消)遍歷元素引用鏈
@Override
@SuppressWarnings("unchecked")
final <P_IN> boolean copyIntoWithCancel(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
@SuppressWarnings({"rawtypes","unchecked"})
AbstractPipeline p = AbstractPipeline.this;
// 基於當前節點,獲取流管道鏈式結構中第最後一個depth=0的前驅節點
while (p.depth > 0) {
p = p.previousStage;
}
wrappedSink.begin(spliterator.getExactSizeIfKnown());
// 委託到forEachWithCancel()進行遍歷
boolean cancelled = p.forEachWithCancel(spliterator, wrappedSink);
wrappedSink.end();
return cancelled;
}
// 返回當前節點的標誌集合
@Override
final int getStreamAndOpFlags() {
return combinedFlags;
}
// 當前節點標誌集合中是否支援ORDERED
final boolean isOrdered() {
return StreamOpFlag.ORDERED.isKnown(combinedFlags);
}
// 構建元素引用鏈,生成一個多重包裝的Sink(WrapSink),這裡的邏輯可以看前面的分析章節
@Override
@SuppressWarnings("unchecked")
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink);
// 這裡遍歷的時候,總是從當前節點向前驅節點遍歷,也就是傳入的sink例項總是包裹在最裡面一層執行
for ( @SuppressWarnings("rawtypes") AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
}
return (Sink<P_IN>) sink;
}
// 包裝資料來源的Spliterator,如果depth=0,則直接返回sourceSpliterator,否則返回的是延遲載入的WrappingSpliterator
@Override
@SuppressWarnings("unchecked")
final <P_IN> Spliterator<E_OUT> wrapSpliterator(Spliterator<P_IN> sourceSpliterator) {
if (depth == 0) {
return (Spliterator<E_OUT>) sourceSpliterator;
}
else {
return wrap(this, () -> sourceSpliterator, isParallel());
}
}
// 計算Node例項,這個方法用於toArray()方法系列,是一個終結操作,下面會另開章節詳細分析
@Override
@SuppressWarnings("unchecked")
final <P_IN> Node<E_OUT> evaluate(Spliterator<P_IN> spliterator,
boolean flatten,
IntFunction<E_OUT[]> generator) {
if (isParallel()) {
// @@@ Optimize if op of this pipeline stage is a stateful op
return evaluateToNode(this, spliterator, flatten, generator);
}
else {
Node.Builder<E_OUT> nb = makeNodeBuilder(
exactOutputSizeIfKnown(spliterator), generator);
return wrapAndCopyInto(nb, spliterator).build();
}
}
// 省略其他方法
}
AbstractPipeline
中剩餘的待如XXYYZZPipeline
等子類實現的抽象方法:
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
// 省略其他方法
// 獲取當前流的輸出"形狀",REFERENCE、INT_VALUE、LONG_VALUE或者DOUBLE_VALUE
abstract StreamShape getOutputShape();
// 收集當前流的所有輸出元素,轉化為一個適配當前流輸出"形狀"的Node例項
abstract <P_IN> Node<E_OUT> evaluateToNode(PipelineHelper<E_OUT> helper,
Spliterator<P_IN> spliterator,
boolean flattenTree,
IntFunction<E_OUT[]> generator);
// 包裝Spliterator為WrappingSpliterator例項
abstract <P_IN> Spliterator<E_OUT> wrap(PipelineHelper<E_OUT> ph,
Supplier<Spliterator<P_IN>> supplier,
boolean isParallel);
// 包裝Spliterator為DelegatingSpliterator例項
abstract <P_IN> Spliterator<E_OUT> wrap(PipelineHelper<E_OUT> ph,
Supplier<Spliterator<P_IN>> supplier,
boolean isParallel);
// 基於Sink遍歷Spliterator中的元素,支援取消操作,簡單理解就是支援cancel的tryAdvance方法
abstract boolean forEachWithCancel(Spliterator<E_OUT> spliterator, Sink<E_OUT> sink);
// 返回Node的建造器例項,用於toArray方法系列
abstract Node.Builder<E_OUT> makeNodeBuilder(long exactSizeIfKnown,
IntFunction<E_OUT[]> generator);
// 判斷當前的操作(節點)是否有狀態,如果是有狀態的操作,必須覆蓋opEvaluateParallel方法
abstract boolean opIsStateful();
// 當前操作生成的結果會作為傳入的Sink例項的入參,這是一個包裝Sink的過程,通俗理解就是之前提到的元素引用鏈新增一個新的鏈節點,這個方法算是流執行的一個核心方法
abstract Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink);
// 併發執行的操作節點求值
<P_IN> Node<E_OUT> opEvaluateParallel(PipelineHelper<E_OUT> helper,
Spliterator<P_IN> spliterator,
IntFunction<E_OUT[]> generator) {
throw new UnsupportedOperationException("Parallel evaluation is not supported");
}
// 併發執行的操作節點惰性求值
@SuppressWarnings("unchecked")
<P_IN> Spliterator<E_OUT> opEvaluateParallelLazy(PipelineHelper<E_OUT> helper,
Spliterator<P_IN> spliterator) {
return opEvaluateParallel(helper, spliterator, i -> (E_OUT[]) new Object[i]).spliterator();
}
// 省略其他方法
}
這裡提到的抽象方法opWrapSink()
其實就是元素引用鏈的新增鏈節點的方法,它的實現邏輯見子類,這裡只考慮非特化子類ReferencePipeline
的部分原始碼:
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 建構函式,用於頭節點,傳入基於Supplier封裝的Spliterator例項作為資料來源,資料來源的標誌集合和是否支援併發執行的判斷標記
ReferencePipeline(Supplier<? extends Spliterator<?>> source,
int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
// 建構函式,用於頭節點,傳入Spliterator例項作為資料來源,資料來源的標誌集合和是否支援併發執行的判斷標記
ReferencePipeline(Spliterator<?> source,
int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
// 建構函式,用於中間節點,傳入上一個流管道節點的例項(前驅節點)和當前操作節點支援的標誌集合
ReferencePipeline(AbstractPipeline<?, P_IN, ?> upstream, int opFlags) {
super(upstream, opFlags);
}
// 這裡流的輸出"形狀"固定為REFERENCE
@Override
final StreamShape getOutputShape() {
return StreamShape.REFERENCE;
}
// 轉換當前流例項為Node例項,應用於toArray方法,後面詳細分析終結操作的時候再展開
@Override
final <P_IN> Node<P_OUT> evaluateToNode(PipelineHelper<P_OUT> helper,
Spliterator<P_IN> spliterator,
boolean flattenTree,
IntFunction<P_OUT[]> generator) {
return Nodes.collect(helper, spliterator, flattenTree, generator);
}
// 包裝Spliterator=>WrappingSpliterator
@Override
final <P_IN> Spliterator<P_OUT> wrap(PipelineHelper<P_OUT> ph,
Supplier<Spliterator<P_IN>> supplier,
boolean isParallel) {
return new StreamSpliterators.WrappingSpliterator<>(ph, supplier, isParallel);
}
// 包裝Spliterator=>DelegatingSpliterator,實現惰性載入
@Override
final Spliterator<P_OUT> lazySpliterator(Supplier<? extends Spliterator<P_OUT>> supplier) {
return new StreamSpliterators.DelegatingSpliterator<>(supplier);
}
// 遍歷Spliterator中的元素,基於傳入的Sink例項進行處理,支援Cancel操作
@Override
final boolean forEachWithCancel(Spliterator<P_OUT> spliterator, Sink<P_OUT> sink) {
boolean cancelled;
do { } while (!(cancelled = sink.cancellationRequested()) && spliterator.tryAdvance(sink));
return cancelled;
}
// 構造Node建造器例項
@Override
final Node.Builder<P_OUT> makeNodeBuilder(long exactSizeIfKnown, IntFunction<P_OUT[]> generator) {
return Nodes.builder(exactSizeIfKnown, generator);
}
// 基於當前流的Spliterator生成迭代器例項
@Override
public final Iterator<P_OUT> iterator() {
return Spliterators.iterator(spliterator());
}
// 省略其他OP的程式碼
// 流管道結構的頭節點
static class Head<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> {
// 建構函式,用於頭節點,傳入基於Supplier封裝的Spliterator例項作為資料來源,資料來源的標誌集合和是否支援併發執行的判斷標記
Head(Supplier<? extends Spliterator<?>> source,
int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
// 建構函式,用於頭節點,傳入Spliterator例項作為資料來源,資料來源的標誌集合和是否支援併發執行的判斷標記
Head(Spliterator<?> source,
int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
// 不支援判斷是否狀態操作
@Override
final boolean opIsStateful() {
throw new UnsupportedOperationException();
}
// 不支援包裝Sink例項
@Override
final Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink) {
throw new UnsupportedOperationException();
}
// 區分同步非同步執行forEach,同步則簡單理解為呼叫Spliterator.forEachRemaining,非同步則呼叫終結操作forEach
@Override
public void forEach(Consumer<? super E_OUT> action) {
if (!isParallel()) {
sourceStageSpliterator().forEachRemaining(action);
}
else {
super.forEach(action);
}
}
// 區分同步非同步執行forEachOrdered,同步則簡單理解為呼叫Spliterator.forEachRemaining,非同步則呼叫終結操作forEachOrdered
@Override
public void forEachOrdered(Consumer<? super E_OUT> action) {
if (!isParallel()) {
sourceStageSpliterator().forEachRemaining(action);
}
else {
super.forEachOrdered(action);
}
}
}
// 無狀態操作節點的父類
abstract static class StatelessOp<E_IN, E_OUT>
extends ReferencePipeline<E_IN, E_OUT> {
// 基於上一個節點引用、輸入元素"形狀"和當前節點支援的標誌集合建立StatelessOp例項
StatelessOp(AbstractPipeline<?, E_IN, ?> upstream,
StreamShape inputShape,
int opFlags) {
super(upstream, opFlags);
assert upstream.getOutputShape() == inputShape;
}
// 操作狀態標記設定為無狀態
@Override
final boolean opIsStateful() {
return false;
}
}
// 有狀態操作節點的父類
abstract static class StatefulOp<E_IN, E_OUT>
extends ReferencePipeline<E_IN, E_OUT> {
// 基於上一個節點引用、輸入元素"形狀"和當前節點支援的標誌集合建立StatefulOp例項
StatefulOp(AbstractPipeline<?, E_IN, ?> upstream,
StreamShape inputShape,
int opFlags) {
super(upstream, opFlags);
assert upstream.getOutputShape() == inputShape;
}
// 操作狀態標記設定為有狀態
@Override
final boolean opIsStateful() {
return true;
}
// 前面也提到,節點操作非同步求值的方法在無狀態節點下必須覆蓋,這裡重新把這個方法抽象,子類必須實現
@Override
abstract <P_IN> Node<E_OUT> opEvaluateParallel(PipelineHelper<E_OUT> helper,
Spliterator<P_IN> spliterator,
IntFunction<E_OUT[]> generator);
}
}
這裡重重重點分析一下ReferencePipeline
中的wrapSink
方法實現:
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink);
for ( @SuppressWarnings("rawtypes") AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
}
return (Sink<P_IN>) sink;
}
入參是一個Sink
例項,返回值也是一個Sink
例項,裡面的for
迴圈是基於當前的AbstractPipeline
節點向前遍歷,直到depth
為0
的節點跳出迴圈,而depth
為0
意味著該節點必定為頭節點,也就是該迴圈是遍歷當前節點到頭節點的後繼節點,Sink
是"向前包裝的",也就是處於鏈後面的節點Sink
總是會作為其前驅節點的opWrapSink()
方法的入參,在同步執行流求值計算的時候,前驅節點的Sink
處理完元素後就會通過downstream
引用(其實就是後驅節點的Sink
)呼叫其accept()
把元素或者處理完的元素結果傳遞進去,啟用下一個Sink
,以此類推。另外,ReferencePipeline
的三個內部類Head
、StatelessOp
和StatefulOp
就是流的節點類,其中只有Head
是非抽象類,代表流管道結構(或者說雙向連結串列結構)的頭節點,StatelessOp
(無狀態操作)和StatefulOp
(有狀態操作)的子類構成了流管道結構的操作節點或者是終結操作。在忽略是否有狀態操作的前提下看ReferencePipeline
,它只是流資料結構的承載體,表面上看到的雙向連結串列結構在流的求值計算過程中並不會進行直接遍歷每個節點進行求值,而是先轉化成一個多層包裝的Sink
,也就是前文筆者提到的元素引用鏈後者前一句分析的Sink
元素處理以及傳遞,正確來說應該是一個Sink
棧或者Sink
包裝器,它的實現可以類比為現實生活中的洋蔥,或者程式設計模式中的AOP
程式設計模式。形象一點的描述如下:
Head(Spliterator) -> Op(filter) -> Op(map) -> Op(sorted) -> Terminal Op(forEach)
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
forEach ele in Spliterator:
Sink[filter](ele){
if filter process == true:
Sink[map](ele){
ele = mapper(ele)
Sink[sorted](ele){
var array
begin:
accept(ele):
add ele to array
end:
sort ele in array
}
}
}
終結操作forEach
是目前分析原始碼中最簡單的實現,下面會詳細分析每種終結操作的實現細節。
流中間操作的原始碼實現
限於篇幅,這裡只能挑選一部分的中間Op
進行分析。流的中間操作基本都是由BaseStream
介面定義,在ReferencePipeline
中進行實現,這裡挑選比較常用的filter
、map
和sorted
進行分析。先看filter
:
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 暫時省略其他程式碼
// filter操作,泛型引數Predicate型別接受一個任意型別(這裡考慮到泛型擦除)的元素,輸出布林值,它是一個無狀態操作
@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
Objects.requireNonNull(predicate);
// 這裡注意到,StatelessOp的第一個引數是指upstream,也就是理解為上一個節點,這裡使用了this,意味著upstream為當前的ReferencePipeline例項,元素"形狀"為引用型別,操作標誌位不支援SIZED
// 在AbstractPipeline,previousStage指向了this,當前的節點就是StatelessOp[filter]例項,那麼前驅節點this的後繼節點引用nextStage就指向了StatelessOp[filter]例項
// 也就是StatelessOp[filter].previousStage = this; this.nextStage = StatelessOp[filter]; ===> 也就是這個看起來簡單的new StatelessOp()其實已經把自身加入到管道中
return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void begin(long size) {
// 這裡通知下一個節點的Sink.begin(),由於filter方法不感知元素數量,所以傳值-1
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
// 基於輸入的Predicate例項判斷當前處理元素是否符合判斷,只有判斷結果為true才會把元素原封不動直接傳遞到下一個Sink
if (predicate.test(u))
downstream.accept(u);
}
};
}
};
}
// 暫時省略其他程式碼
}
接著是map
:
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 暫時省略其他程式碼
// map操作,基於傳入的Function例項做對映轉換(P_OUT->R),它是一個無狀態操作
@Override
@SuppressWarnings("unchecked")
public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
Objects.requireNonNull(mapper);
// upstream為當前的ReferencePipeline例項,元素"形狀"為引用型別,操作標誌位不支援SORTED和DISTINCT
return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<P_OUT, R>(sink) {
@Override
public void accept(P_OUT u) {
// 基於傳入的Function例項轉換元素後把轉換結果傳遞到下一個Sink
downstream.accept(mapper.apply(u));
}
};
}
};
}
// 暫時省略其他程式碼
}
然後是sorted
,sorted
操作會相對複雜一點:
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 暫時省略其他程式碼
// sorted操作,基於傳入的Comparator例項對處理的元素進行排序,從原始碼中看,它是一個有狀態操作
@Override
public final Stream<P_OUT> sorted(Comparator<? super P_OUT> comparator) {
return SortedOps.makeRef(this, comparator);
}
// 暫時省略其他程式碼
}
// SortedOps工具類
final class SortedOps {
// 暫時省略其他程式碼
// 構建排序操作的鏈節點
static <T> Stream<T> makeRef(AbstractPipeline<?, T, ?> upstream,
Comparator<? super T> comparator) {
return new OfRef<>(upstream, comparator);
}
// 有狀態的排序操作節點
private static final class OfRef<T> extends ReferencePipeline.StatefulOp<T, T> {
// 是否自然排序,不定義Comparator例項的時候為true,否則為false
private final boolean isNaturalSort;
// 用於排序的Comparator例項
private final Comparator<? super T> comparator;
// 自然排序情況下的構造方法,元素"形狀"為引用型別,操作標誌位不支援ORDERED和SORTED
OfRef(AbstractPipeline<?, T, ?> upstream) {
super(upstream, StreamShape.REFERENCE,
StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
this.isNaturalSort = true;
// Comparator例項賦值為Comparator.naturalOrder(),本質是基於Object中的equals或者子類覆蓋Object中的equals方法進行元素排序
@SuppressWarnings("unchecked")
Comparator<? super T> comp = (Comparator<? super T>) Comparator.naturalOrder();
this.comparator = comp;
}
// 非自然排序情況下的構造方法,需要傳入Comparator例項,元素"形狀"為引用型別,操作標誌位不支援ORDERED和SORTED
OfRef(AbstractPipeline<?, T, ?> upstream, Comparator<? super T> comparator) {
super(upstream, StreamShape.REFERENCE,
StreamOpFlag.IS_ORDERED | StreamOpFlag.NOT_SORTED);
this.isNaturalSort = false;
this.comparator = Objects.requireNonNull(comparator);
}
@Override
public Sink<T> opWrapSink(int flags, Sink<T> sink) {
Objects.requireNonNull(sink);
// If the input is already naturally sorted and this operation
// also naturally sorted then this is a no-op
// 流中的所有元素本身已經按照自然順序排序,並且沒有定義Comparator例項,則不需要進行排序,所以no op就行
if (StreamOpFlag.SORTED.isKnown(flags) && isNaturalSort)
return sink;
else if (StreamOpFlag.SIZED.isKnown(flags))
// 知道要處理的元素的確切數量,使用陣列進行排序
return new SizedRefSortingSink<>(sink, comparator);
else
// 不知道要處理的元素的確切數量,使用ArrayList進行排序
return new RefSortingSink<>(sink, comparator);
}
// 這裡是並行執行流中toArray方法的實現,暫不分析
@Override
public <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
Spliterator<P_IN> spliterator,
IntFunction<T[]> generator) {
// If the input is already naturally sorted and this operation
// naturally sorts then collect the output
if (StreamOpFlag.SORTED.isKnown(helper.getStreamAndOpFlags()) && isNaturalSort) {
return helper.evaluate(spliterator, false, generator);
}
else {
// @@@ Weak two-pass parallel implementation; parallel collect, parallel sort
T[] flattenedData = helper.evaluate(spliterator, true, generator).asArray(generator);
Arrays.parallelSort(flattenedData, comparator);
return Nodes.node(flattenedData);
}
}
}
// 這裡考慮到篇幅太長,SizedRefSortingSink和RefSortingSink的原始碼不復雜,只展開RefSortingSink進行分析
// 無法確認待處理元素確切數量時候用於元素排序的Sink實現
private static final class RefSortingSink<T> extends AbstractRefSortingSink<T> {
// 臨時ArrayList例項
private ArrayList<T> list;
// 建構函式,需要的引數為下一個Sink引用和Comparator例項
RefSortingSink(Sink<? super T> sink, Comparator<? super T> comparator) {
super(sink, comparator);
}
@Override
public void begin(long size) {
if (size >= Nodes.MAX_ARRAY_SIZE)
throw new IllegalArgumentException(Nodes.BAD_SIZE);
// 基於傳入的size是否大於0,大於等於0用於作為initialCapacity構建ArrayList,小於0則構建預設initialCapacity的ArrayList,賦值到臨時變數list
list = (size >= 0) ? new ArrayList<>((int) size) : new ArrayList<>();
}
@Override
public void end() {
// 臨時的ArrayList例項基於Comparator例項進行潘旭
list.sort(comparator);
// 下一個Sink節點的啟用,區分是否支援取消操作
downstream.begin(list.size());
if (!cancellationRequestedCalled) {
list.forEach(downstream::accept);
}
else {
for (T t : list) {
if (downstream.cancellationRequested()) break;
downstream.accept(t);
}
}
downstream.end();
// 啟用下一個Sink完成後,臨時的ArrayList例項置為NULL,便於GC回收
list = null;
}
@Override
public void accept(T t) {
// 當前Sink處理元素直接新增到臨時的ArrayList例項
list.add(t);
}
}
// 暫時省略其他程式碼
}
sorted
操作有個比較顯著的特點,一般的Sink
處理完自身的邏輯,會在accept()
方法啟用下一個Sink
引用,但是它在accept()
方法中只做元素的累積(元素富集),在end()
方法進行最終的排序操作和模仿Spliterator
的兩個元素遍歷方法向downstream
推送待處理的元素。示意圖如下:
其他中間操作的實現邏輯是大致相同的。
同步執行流終結操作的原始碼實現
限於篇幅,這裡只能挑選一部分的Terminal Op
進行分析,簡單起見只分析同步執行的場景,這裡挑選最典型和最複雜的froEach()
和collect()
,還有比較獨特的toArray()
方法。先看froEach()
方法的實現過程:
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 暫時省略其他程式碼
// 遍歷元素
@Override
public void forEach(Consumer<? super P_OUT> action) {
evaluate(ForEachOps.makeRef(action, false));
}
// 暫時省略其他程式碼
// 基於終結操作的求值方法
final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
assert getOutputShape() == terminalOp.inputShape();
// 確保只會執行一次,linkedOrConsumed是流管道結構最後一個節點的屬性
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
// 這裡暫且只分析同步執行的流的終結操作,終結操作節點的標誌會合併到流最後一個節點的combinedFlags中,執行的關鍵就是evaluateSequential方法
return isParallel()
? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
: terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
}
// 暫時省略其他程式碼
}
// ForEachOps類,TerminalOp介面的定義比較簡單,這裡不展開
final class ForEachOps {
// 暫時省略其他程式碼
// 構造變數元素的終結操作例項,傳入的元素是T型別,結果是Void型別(返回NULL,或者說是沒有返回值,畢竟是一個元素遍歷過程)
// 引數為一個Consumer介面例項和一個標記是否順序處理元素的布林值
public static <T> TerminalOp<T, Void> makeRef(Consumer<? super T> action,
boolean ordered) {
Objects.requireNonNull(action);
return new ForEachOp.OfRef<>(action, ordered);
}
// 遍歷元素操作的終結操作實現,同時它是一個介面卡,適配TerminalSink(Sink)介面
abstract static class ForEachOp<T>
implements TerminalOp<T, Void>, TerminalSink<T, Void> {
// 標記是否順序處理元素
private final boolean ordered;
protected ForEachOp(boolean ordered) {
this.ordered = ordered;
}
// TerminalOp
// 終結操作節點的標誌集合,如果ordered為true則返回0,否則返回StreamOpFlag.NOT_ORDERED,表示不支援順序處理元素
@Override
public int getOpFlags() {
return ordered ? 0 : StreamOpFlag.NOT_ORDERED;
}
// 同步遍歷和處理元素
@Override
public <S> Void evaluateSequential(PipelineHelper<T> helper,
Spliterator<S> spliterator) {
// 以當前的ForEachOp例項作為最後一個Sink新增到Sink鏈(也就是前面經常說的元素引用鏈),然後對Sink鏈進行遍歷
return helper.wrapAndCopyInto(this, spliterator).get();
}
// 併發遍歷和處理元素,這裡暫不分析
@Override
public <S> Void evaluateParallel(PipelineHelper<T> helper,
Spliterator<S> spliterator) {
if (ordered)
new ForEachOrderedTask<>(helper, spliterator, this).invoke();
else
new ForEachTask<>(helper, spliterator, helper.wrapSink(this)).invoke();
return null;
}
// TerminalSink
// 實現TerminalSink的方法,實際上TerminalSink繼承介面Supplier,這裡是實現了Supplier介面的get()方法,由於PipelineHelper.wrapAndCopyInto()方法會返回最後一個Sink的引用,這裡其實就是evaluateSequential()中的返回值
@Override
public Void get() {
return null;
}
// ForEachOp的靜態內部類,引用型別的ForEachOp的最終實現,依賴入參遍歷元素處理的最後一步回撥Consumer例項
static final class OfRef<T> extends ForEachOp<T> {
// 最後的遍歷回撥的Consumer控制程式碼
final Consumer<? super T> consumer;
OfRef(Consumer<? super T> consumer, boolean ordered) {
super(ordered);
this.consumer = consumer;
}
@Override
public void accept(T t) {
// 遍歷元素回撥操作
consumer.accept(t);
}
}
}
}
forEach
終結操作實現上,自身這個操作並不會構成流的鏈式結構的一部分,也就是它不是一個AbstractPipeline
的子類例項,而是構建一個回撥Consumer
例項操作的一個Sink
例項(準確來說是TerminalSink
)例項,這裡暫且叫forEach terminal sink
,通過流最後一個操作節點的wrapSink()
方法,把forEach terminal sink
新增到Sink
鏈的尾部,通過流最後一個操作節點的copyInto()
方法進行元素遍歷,按照copyInto()
方法的套路,只要多層包裝的Sink
方法在回撥其實現方法的時候總是啟用downstream
的前提下,執行的順序就是流鏈式結構定義的操作節點順序,而forEach
最後新增的Consumer
例項一定就是最後回撥的。
接著分析collect()
方法的實現,先看Collector
介面的定義:
// T:需要進行reduce操作的輸入元素型別
// A:reduce操作中可變累加物件的型別,可以簡單理解為累加操作中,累加到Container<A>中的可變物件型別
// R:reduce操作結果型別
public interface Collector<T, A, R> {
// 註釋中稱為Container,用於承載最終結果的可變容器,而此方法的Supplier例項持有的是建立Container例項的get()方法實現,後面稱為Supplier
// 也就是一般使用如:Supplier<Container> supplier = () -> new Container();
Supplier<A> supplier();
// Accumulator,翻譯為累加器,用於處理值並且把處理結果傳遞(累加)到Container中,後面稱為Accumulator
BiConsumer<A, T> accumulator();
// Combiner,翻譯為合併器,真實泛型型別為BinaryOperator<A,A,A>,BiFunction的子類,接收兩個部分的結果並且合併為一個結果,後面稱為Combiner
// 這個方法可以把一個引數的狀態轉移到另一個引數,然後返回更新狀態後的引數,例如:(arg1, arg2) -> {arg2.state = arg1.state; return arg2;}
// 可以把一個引數的狀態轉移到另一個引數,然後返回一個新的容器,例如:(arg1, arg2) -> {arg2.state = arg1.state; return new Container(arg2);}
BinaryOperator<A> combiner();
// Finisher,直接翻譯感覺意義不合理,實際上就是做最後一步轉換工作的處理器,後面稱為Finisher
Function<A, R> finisher();
// Collector支援的特性集合,見列舉Characteristics
Set<Characteristics> characteristics();
// 這裡忽略兩個Collector的靜態工廠方法,因為並不常用
enum Characteristics {
// 標記Collector支援併發執行,一般和併發容器相關
CONCURRENT,
// 標記Collector處理元素時候無序
UNORDERED,
// 標記Collector的輸入和輸出元素是同型別,也就是Finisher在實現上R -> A可以等效於A -> R,unchecked cast會成功(也就是型別強轉可以成功)
// 在這種場景下,對於Container來說其實型別強制轉換也是等效的,也就是Supplier<A>和Supplier<R>得出的Container是同一種型別的Container
IDENTITY_FINISH
}
}
// Collector的實現Collectors.CollectorImpl
public final class Collectors {
// 這一大堆常量就是預設的多種特性組合,CH_NOID比較特殊,是空集合,也就是Collector三種特性都不支援
static final Set<Collector.Characteristics> CH_CONCURRENT_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_CONCURRENT_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED));
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_UNORDERED_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
Collector.Characteristics.IDENTITY_FINISH));
static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
static final Set<Collector.Characteristics> CH_UNORDERED_NOID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED));
private Collectors() { }
// 省略大量程式碼
// 靜態類,Collector的實現,實現其實就是Supplier、Accumulator、Combiner、Finisher和Characteristics集合的成員屬性承載
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;
CollectorImpl(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A,R> finisher,
Set<Characteristics> characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
CollectorImpl(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Set<Characteristics> characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
@Override
public BiConsumer<A, T> accumulator() {
return accumulator;
}
@Override
public Supplier<A> supplier() {
return supplier;
}
@Override
public BinaryOperator<A> combiner() {
return combiner;
}
@Override
public Function<A, R> finisher() {
return finisher;
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
}
// 省略大量程式碼
// IDENTITY_FINISH特性下,Finisher的實現,也就是之前提到的A->R和R->A等效,可以強轉
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
// 省略大量程式碼
}
collect()
方法的求值執行入口在ReferencePipeline
中:
// ReferencePipeline
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 暫時省略其他程式碼
// 基於Collector例項進行求值
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
// 併發求值場景暫不考慮
if (isParallel()
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&& (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
forEach(u -> accumulator.accept(container, u));
}
else {
// 這裡就是同步執行場景下的求值過程,這裡可以看出其實所有Collector的求值都是Reduce操作
container = evaluate(ReduceOps.makeRef(collector));
}
// 如果Collector的Finisher輸入型別和輸出型別相同,所以Supplier<A>和Supplier<R>得出的Container是同一種型別的Container,可以直接型別轉換,否則就要呼叫Collector中的Finisher進行最後一步處理
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}
// 暫時省略其他程式碼
}
// ReduceOps
final class ReduceOps {
private ReduceOps() { }
// 暫時省略其他程式碼
// 引用型別Reduce操作建立TerminalOp例項
public static <T, I> TerminalOp<T, I>
makeRef(Collector<? super T, I, ?> collector) {
// Supplier
Supplier<I> supplier = Objects.requireNonNull(collector).supplier();
// Accumulator
BiConsumer<I, ? super T> accumulator = collector.accumulator();
// Combiner
BinaryOperator<I> combiner = collector.combiner();
// 這裡注意一點,ReducingSink是方法makeRef中的內部類,作用域只在方法內,它是封裝為TerminalOp最終轉化為Sink鏈中最後一個Sink例項的型別
class ReducingSink extends Box<I>
implements AccumulatingSink<T, I, ReducingSink> {
@Override
public void begin(long size) {
// 這裡把從Supplier建立的新Container例項存放在父類Box的狀態屬性中
state = supplier.get();
}
@Override
public void accept(T t) {
// 處理元素,Accumulator處理狀態(容器例項)和元素,這裡可以想象,如果state為一個ArrayList例項,這裡的accept()實現可能為add(ele)操作
accumulator.accept(state, t);
}
@Override
public void combine(ReducingSink other) {
// Combiner合併兩個狀態(容器例項)
state = combiner.apply(state, other.state);
}
}
return new ReduceOp<T, I, ReducingSink>(StreamShape.REFERENCE) {
@Override
public ReducingSink makeSink() {
return new ReducingSink();
}
@Override
public int getOpFlags() {
return collector.characteristics().contains(Collector.Characteristics.UNORDERED)
? StreamOpFlag.NOT_ORDERED
: 0;
}
};
}
// 暫時省略其他程式碼
// 繼承自介面TerminalSink,主要新增了combine()抽象方法,用於合併元素
private interface AccumulatingSink<T, R, K extends AccumulatingSink<T, R, K>>
extends TerminalSink<T, R> {
void combine(K other);
}
// 狀態盒,用於持有和獲取狀態,狀態屬性的修飾符為default,包內的類例項都能修改
private abstract static class Box<U> {
U state;
Box() {} // Avoid creation of special accessor
public U get() {
return state;
}
}
// ReduceOp的最終實現,這個就是Reduce操作終結操作的實現
private abstract static class ReduceOp<T, R, S extends AccumulatingSink<T, R, S>>
implements TerminalOp<T, R> {
// 流輸入元素"形狀"
private final StreamShape inputShape;
ReduceOp(StreamShape shape) {
inputShape = shape;
}
// 抽象方法,讓子類生成終結操作的Sink
public abstract S makeSink();
// 獲取流輸入元素"形狀"
@Override
public StreamShape inputShape() {
return inputShape;
}
// 同步執行求值,還是相似的思路,使用wrapAndCopyInto()進行Sink鏈構建和元素遍歷
@Override
public <P_IN> R evaluateSequential(PipelineHelper<T> helper,
Spliterator<P_IN> spliterator) {
// 以當前的ReduceOp例項的makeSink()返回的Sink例項作為最後一個Sink新增到Sink鏈(也就是前面經常說的元素引用鏈),然後對Sink鏈進行遍歷
// 這裡向上一步一步推演思考,最終get()方法執行完畢拿到的結果就是ReducingSink父類Box中的state變數,也就是容器例項
return helper.wrapAndCopyInto(makeSink(), spliterator).get();
}
// 非同步執行求值,暫時忽略
@Override
public <P_IN> R evaluateParallel(PipelineHelper<T> helper,
Spliterator<P_IN> spliterator) {
return new ReduceTask<>(this, helper, spliterator).invoke().get();
}
}
// 暫時省略其他程式碼
}
接著就看Collector
的靜態工廠方法,看一些常用的Collector
例項是如何構建的,例如看Collectors.toList()
:
// Supplier => () -> new ArrayList<T>(); // 初始化ArrayList
// Accumulator => (list,number) -> list.add(number); // 往ArrayList中新增元素
// Combiner => (left, right) -> { left.addAll(right); return left;} // 合併ArrayList
// Finisher => X -> X; // 輸入什麼就返回什麼,這裡實際返回的是ArrayList
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
把過程畫成流程圖如下:
甚至可以更通俗地用虛擬碼表示Collector
這類Terminal Op
的執行過程(還是以Collectors.toList()
為例):
[begin]
Supplier supplier = () -> new ArrayList<T>();
Container container = supplier.get();
Box.state = container;
[accept]
Box.state.add(element);
[end]
return supplier.get(); (=> return Box.state);
↓↓↓↓↓↓↓↓↓甚至更加通俗的過程如下↓↓↓↓↓↓↓↓↓↓↓↓↓↓
ArrayList<T> container = new ArrayList<T>();
loop:
container.add(element)
return container;
也就是雖然工程化的程式碼看起來很複雜,最終的實現就是簡單的:初始化ArrayList
例項由state
屬性持有,遍歷處理元素的時候把元素新增到state
中,最終返回state
。最後看toArray()
的方法實現(下面的方法程式碼沒有按照實際的位置貼出,筆者把零散的程式碼塊放在一起方便分析):
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
// 暫時省略其他程式碼
// 流的所有元素轉換為陣列,這裡的IntFunction有一種比較特殊的用法,就是用於建立陣列例項
// 例如IntFunction<String[]> f = String::new; String[] arry = f.apply(2); 相當於String[] arry = new String[2];
@Override
@SuppressWarnings("unchecked")
public final <A> A[] toArray(IntFunction<A[]> generator) {
// 這裡主動擦除了IntFunction的型別,只要保證求值的過程是正確,最終可以做型別強轉
@SuppressWarnings("rawtypes")
IntFunction rawGenerator = (IntFunction) generator;
// 委託到evaluateToArrayNode()方法進行計算
return (A[]) Nodes.flatten(evaluateToArrayNode(rawGenerator), rawGenerator)
.asArray(rawGenerator);
}
// 流的所有元素轉換為Object陣列
@Override
public final Object[] toArray() {
return toArray(Object[]::new);
}
// 流元素求值轉換為ArrayNode
final Node<E_OUT> evaluateToArrayNode(IntFunction<E_OUT[]> generator) {
// 確保不會處理多次
if (linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
// 併發執行暫時跳過
if (isParallel() && previousStage != null && opIsStateful()) {
depth = 0;
return opEvaluateParallel(previousStage, previousStage.sourceSpliterator(0), generator);
}
else {
return evaluate(sourceSpliterator(0), true, generator);
}
}
// 最終的轉換Node的方法
final <P_IN> Node<E_OUT> evaluate(Spliterator<P_IN> spliterator,
boolean flatten,
IntFunction<E_OUT[]> generator) {
// 併發執行暫時跳過
if (isParallel()) {
// @@@ Optimize if op of this pipeline stage is a stateful op
return evaluateToNode(this, spliterator, flatten, generator);
}
else {
// 兜兜轉換還是回到了wrapAndCopyInto()方法,遍歷Sink鏈,所以基本可以得知Node.Builder是Sink的一個實現
Node.Builder<E_OUT> nb = makeNodeBuilder(
exactOutputSizeIfKnown(spliterator), generator);
return wrapAndCopyInto(nb, spliterator).build();
}
}
// 獲取Node的建造器例項
final Node.Builder<P_OUT> makeNodeBuilder(long exactSizeIfKnown, IntFunction<P_OUT[]> generator) {
return Nodes.builder(exactSizeIfKnown, generator);
}
// 暫時省略其他程式碼
}
// Node介面定義
interface Node<T> {
// 獲取待處理的元素封裝成的Spliterator例項
Spliterator<T> spliterator();
// 遍歷當前Node例項中所有待處理的元素,回撥到Consumer例項中
void forEach(Consumer<? super T> consumer);
// 獲取當前Node例項的所有子Node的個數
default int getChildCount() {
return 0;
}
// 獲取當前Node例項的子Node例項,入參i是子Node的索引
default Node<T> getChild(int i) {
throw new IndexOutOfBoundsException();
}
// 分割當前Node例項的一個部分,生成一個新的sub Node,類似於ArrayList中的subList方法
default Node<T> truncate(long from, long to, IntFunction<T[]> generator) {
if (from == 0 && to == count())
return this;
Spliterator<T> spliterator = spliterator();
long size = to - from;
Node.Builder<T> nodeBuilder = Nodes.builder(size, generator);
nodeBuilder.begin(size);
for (int i = 0; i < from && spliterator.tryAdvance(e -> { }); i++) { }
if (to == count()) {
spliterator.forEachRemaining(nodeBuilder);
} else {
for (int i = 0; i < size && spliterator.tryAdvance(nodeBuilder); i++) { }
}
nodeBuilder.end();
return nodeBuilder.build();
}
// 建立一個包含當前Node例項所有元素的元素陣列檢視
T[] asArray(IntFunction<T[]> generator);
//
void copyInto(T[] array, int offset);
// 返回Node例項基於Stream的元素"形狀"
default StreamShape getShape() {
return StreamShape.REFERENCE;
}
// 獲取當前Node例項包含的元素個數
long count();
// Node建造器,注意這個Node.Builder介面是繼承自Sink,那麼其子類實現都可以新增到Sink鏈中作為一個節點(終結節點)
interface Builder<T> extends Sink<T> {
// 建立Node例項
Node<T> build();
// 基於Integer元素型別的特化型別Node.Builder
interface OfInt extends Node.Builder<Integer>, Sink.OfInt {
@Override
Node.OfInt build();
}
// 基於Long元素型別的特化型別Node.Builder
interface OfLong extends Node.Builder<Long>, Sink.OfLong {
@Override
Node.OfLong build();
}
// 基於Double元素型別的特化型別Node.Builder
interface OfDouble extends Node.Builder<Double>, Sink.OfDouble {
@Override
Node.OfDouble build();
}
}
// 暫時省略其他程式碼
}
// 這裡下面的方法來源於Nodes類
final class Nodes {
// 暫時省略其他程式碼
// Node扁平化處理,如果傳入的Node例項存在子Node例項,則使用fork-join對Node進行分割和併發計算,結果新增到IntFunction生成的陣列中,如果不存在子Node,直接返回傳入的Node例項
// 關於併發計算部分暫時不分析
public static <T> Node<T> flatten(Node<T> node, IntFunction<T[]> generator) {
if (node.getChildCount() > 0) {
long size = node.count();
if (size >= MAX_ARRAY_SIZE)
throw new IllegalArgumentException(BAD_SIZE);
T[] array = generator.apply((int) size);
new ToArrayTask.OfRef<>(node, array, 0).invoke();
return node(array);
} else {
return node;
}
}
// 建立Node的建造器例項
static <T> Node.Builder<T> builder(long exactSizeIfKnown, IntFunction<T[]> generator) {
// 當知道待處理元素的準確數量並且小於允許建立的陣列的最大長度MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),使用FixedNodeBuilder(固定長度陣列Node建造器),否則使用SpinedNodeBuilder例項
return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
? new FixedNodeBuilder<>(exactSizeIfKnown, generator)
: builder();
}
// 建立Node的建造器例項,使用SpinedNodeBuilder的例項,此SpinedNode支援元素新增,但是不支援元素移除
static <T> Node.Builder<T> builder() {
return new SpinedNodeBuilder<>();
}
// 固定長度固定長度陣列Node實現(也就是最終的Node實現是一個ArrayNode,最終的容器為一個T型別元素的陣列T[])
private static final class FixedNodeBuilder<T>
extends ArrayNode<T>
implements Node.Builder<T> {
// 基於size(元素個數,或者說建立陣列的長度)和陣列建立方法IntFunction構建FixedNodeBuilder例項
FixedNodeBuilder(long size, IntFunction<T[]> generator) {
super(size, generator);
assert size < MAX_ARRAY_SIZE;
}
// 返回當前FixedNodeBuilder例項,判斷陣列元素計數值curSize必須大於等於實際陣列容器中元素的個數
@Override
public Node<T> build() {
if (curSize < array.length)
throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
curSize, array.length));
return this;
}
// Sink的begin方法回撥,傳入的size必須和陣列長度相等,因為後面的accept()方法會執行size此
@Override
public void begin(long size) {
if (size != array.length)
throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
size, array.length));
// 重置陣列元素計數值為0
curSize = 0;
}
// Sink的accept方法回撥,當陣列元素計數值小於陣列長度,直接向陣列下標curSize++新增傳入的元素
@Override
public void accept(T t) {
if (curSize < array.length) {
array[curSize++] = t;
} else {
throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
array.length));
}
}
// Sink的end方法回撥,再次判斷陣列元素計數值curSize必須大於等於實際陣列容器中元素的個數
@Override
public void end() {
if (curSize < array.length)
throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
curSize, array.length));
}
// 返回FixedNodeBuilder當前資訊,當前處理的下標和當前陣列中所有的元素
@Override
public String toString() {
return String.format("FixedNodeBuilder[%d][%s]",
array.length - curSize, Arrays.toString(array));
}
}
// Node實現,容器為一個固定長度的陣列
private static class ArrayNode<T> implements Node<T> {
// 陣列容器
final T[] array;
// 陣列容器中當前元素的個數,這個值是一個固定值,或者在FixedNodeBuilder的accept()方法回撥中遞增
int curSize;
// 基於size和陣列建立的工廠IntFunction構建ArrayNode例項
@SuppressWarnings("unchecked")
ArrayNode(long size, IntFunction<T[]> generator) {
if (size >= MAX_ARRAY_SIZE)
throw new IllegalArgumentException(BAD_SIZE);
// 建立szie長度的陣列容器
this.array = generator.apply((int) size);
this.curSize = 0;
}
// 這個方法是基於一個現成的陣列建立ArrayNode例項,直接改變陣列的引用為array,元素個數curSize置為輸入引數長度
ArrayNode(T[] array) {
this.array = array;
this.curSize = array.length;
}
// Node - 接下來是Node介面的實現
// 基於陣列例項,起始索引0和結束索引curSize構造一個全新的Spliterator例項
@Override
public Spliterator<T> spliterator() {
return Arrays.spliterator(array, 0, curSize);
}
// 拷貝array中的元素到外部傳入的dest陣列中
@Override
public void copyInto(T[] dest, int destOffset) {
System.arraycopy(array, 0, dest, destOffset, curSize);
}
// 返回元素陣列檢視,這裡直接返回array引用
@Override
public T[] asArray(IntFunction<T[]> generator) {
if (array.length == curSize) {
return array;
} else {
throw new IllegalStateException();
}
}
// 獲取array中的元素個數
@Override
public long count() {
return curSize;
}
// 遍歷array,每個元素回撥Consumer例項
@Override
public void forEach(Consumer<? super T> consumer) {
for (int i = 0; i < curSize; i++) {
consumer.accept(array[i]);
}
}
// 返回ArrayNode當前資訊,當前處理的下標和當前陣列中所有的元素
@Override
public String toString() {
return String.format("ArrayNode[%d][%s]",
array.length - curSize, Arrays.toString(array));
}
}
// 暫時省略其他程式碼
}
很多集合容器的Spliterator
其實並不支援SIZED
特性,其實Node
的最終實現很多情況下都是Nodes.SpinedNodeBuilder
,因為SpinedNodeBuilder
重實現實現了陣列擴容和Spliterator
基於陣列進行分割的方法,原始碼相對複雜(特別是spliterator()
方法),這裡挑部分進行分析,由於SpinedNodeBuilder
絕大部分方法都是使用父類SpinedBuffer
中的實現,這裡可以直接分析SpinedBuffer
:
// SpinedBuffer的當前陣列在超過了元素數量閾值之後,會拆分為多個陣列塊,儲存到spine中,而curChunk引用指向的是當前處理的陣列塊
class SpinedBuffer<E>
extends AbstractSpinedBuffer
implements Consumer<E>, Iterable<E> {
// 暫時省略其他程式碼
// 當前的陣列塊
protected E[] curChunk;
// 所有陣列塊
protected E[][] spine;
// 建構函式,指定初始化容量
SpinedBuffer(int initialCapacity) {
super(initialCapacity);
curChunk = (E[]) new Object[1 << initialChunkPower];
}
// 建構函式,指定預設初始化容量
@SuppressWarnings("unchecked")
SpinedBuffer() {
super();
curChunk = (E[]) new Object[1 << initialChunkPower];
}
// 拷貝當前SpinedBuffer中的陣列元素到傳入的陣列例項
public void copyInto(E[] array, int offset) {
// 計算最終的offset,區分單個chunk和多個chunk的情況
long finalOffset = offset + count();
if (finalOffset > array.length || finalOffset < offset) {
throw new IndexOutOfBoundsException("does not fit");
}
// 單個chunk的情況,由curChunk最接拷貝
if (spineIndex == 0)
System.arraycopy(curChunk, 0, array, offset, elementIndex);
else {
// 多個chunk的情況,由遍歷spine並且對每個chunk進行拷貝
// full chunks
for (int i=0; i < spineIndex; i++) {
System.arraycopy(spine[i], 0, array, offset, spine[i].length);
offset += spine[i].length;
}
if (elementIndex > 0)
System.arraycopy(curChunk, 0, array, offset, elementIndex);
}
}
// 返回陣列元素檢視,基於IntFunction構建陣列例項,使用copyInto()方法進行元素拷貝
public E[] asArray(IntFunction<E[]> arrayFactory) {
long size = count();
if (size >= Nodes.MAX_ARRAY_SIZE)
throw new IllegalArgumentException(Nodes.BAD_SIZE);
E[] result = arrayFactory.apply((int) size);
copyInto(result, 0);
return result;
}
// 清空SpinedBuffer,清空分塊元素和所有引用
@Override
public void clear() {
if (spine != null) {
curChunk = spine[0];
for (int i=0; i<curChunk.length; i++)
curChunk[i] = null;
spine = null;
priorElementCount = null;
}
else {
for (int i=0; i<elementIndex; i++)
curChunk[i] = null;
}
elementIndex = 0;
spineIndex = 0;
}
// 遍歷元素回撥Consumer,分別遍歷spine和curChunk
@Override
public void forEach(Consumer<? super E> consumer) {
// completed chunks, if any
for (int j = 0; j < spineIndex; j++)
for (E t : spine[j])
consumer.accept(t);
// current chunk
for (int i=0; i<elementIndex; i++)
consumer.accept(curChunk[i]);
}
// Consumer的accept實現,最終會作為Sink介面的accept方法呼叫
@Override
public void accept(E e) {
// 如果當前分塊(第一個)的元素已經滿了,就初始化spine,然後元素新增到spine[0]中
if (elementIndex == curChunk.length) {
inflateSpine();
// 然後元素新增到spine[0]中的元素已經滿了,就新增spine[n],把元素放進spine[n]中
if (spineIndex+1 >= spine.length || spine[spineIndex+1] == null)
increaseCapacity();
elementIndex = 0;
++spineIndex;
// 當前的chunk更新為最新的chunk,就是spine中的最新一個chunk
curChunk = spine[spineIndex];
}
// 當前的curChunk新增元素
curChunk[elementIndex++] = e;
}
// 暫時省略其他程式碼
}
原始碼已經基本分析完畢,下面還是用一個例子轉化為流程圖:
流併發執行的原始碼實現
如果流例項呼叫了parallel()
,註釋中提到會返回一個非同步執行流的變體,實際上並沒有構造變體,只是把sourceStage.parallel
標記為true
,非同步求值的基本過程是:構建流管道結構的時候和同步求值的過程一致,構建完Sink
鏈之後,Spliterator
會使用特定演算法基於trySplit()
進行自分割,自分割演算法由具體的子類決定,例如ArrayList
採用的就是二分法,分割完成後每個Spliterator
持有所有元素中的一小部分,然後把每個Spliterator
作為sourceSpliterator
在fork-join
執行緒池中執行Sink
鏈,得到多個部分的結果在當前呼叫執行緒中聚合,得到最終結果。這裡用到的技巧就是:執行緒封閉和fork-join
。因為不同Terminal Op
的併發求值過程大同小異,這裡只分析forEach
併發執行的實現。首先展示一個使用fork-join
執行緒池的簡單例子:
public class MapReduceApp {
public static void main(String[] args) {
// 陣列中每個元素*2,再求和
Integer result = new MapReducer<>(new Integer[]{1, 2, 3, 4}, x -> x * 2, Integer::sum).invoke();
System.out.println(result);
}
interface Mapper<S, T> {
T apply(S source);
}
interface Reducer<S, T> {
T apply(S first, S second);
}
public static class MapReducer<T> extends CountedCompleter<T> {
final T[] array;
final Mapper<T, T> mapper;
final Reducer<T, T> reducer;
final int lo, hi;
MapReducer<T> sibling;
T result;
public MapReducer(T[] array,
Mapper<T, T> mapper,
Reducer<T, T> reducer) {
this.array = array;
this.mapper = mapper;
this.reducer = reducer;
this.lo = 0;
this.hi = array.length;
}
public MapReducer(CountedCompleter<?> p,
T[] array,
Mapper<T, T> mapper,
Reducer<T, T> reducer,
int lo,
int hi) {
super(p);
this.array = array;
this.mapper = mapper;
this.reducer = reducer;
this.lo = lo;
this.hi = hi;
}
@Override
public void compute() {
if (hi - lo >= 2) {
int mid = (lo + hi) >> 1;
MapReducer<T> left = new MapReducer<>(this, array, mapper, reducer, lo, mid);
MapReducer<T> right = new MapReducer<>(this, array, mapper, reducer, mid, hi);
left.sibling = right;
right.sibling = left;
// 建立子任務父任務的pending計數器加1
setPendingCount(1);
// 提交右子任務
right.fork();
// 在當前執行緒計算左子任務
left.compute();
} else {
if (hi > lo) {
result = mapper.apply(array[lo]);
}
// 葉子節點完成,嘗試合併其他兄弟節點的結果,會呼叫onCompletion方法
tryComplete();
}
}
@Override
public T getRawResult() {
return result;
}
@SuppressWarnings("unchecked")
@Override
public void onCompletion(CountedCompleter<?> caller) {
if (caller != this) {
MapReducer<T> child = (MapReducer<T>) caller;
MapReducer<T> sib = child.sibling;
// 合併子任務結果,只有兩個子任務
if (Objects.isNull(sib) || Objects.isNull(sib.result)) {
result = child.result;
} else {
result = reducer.apply(child.result, sib.result);
}
}
}
}
}
這裡簡單使用了fork-join
編寫了一個簡易的MapReduce
應用,main
方法中執行的是陣列[1,2,3,4]
中的所有元素先對映為i -> i * 2
,再進行reduce
(求和)的過程,程式碼中也是簡單使用二分法對原始的array
進行分割,當最終的任務只包含一個元素,也就是lo < hi
且hi - lo == 1
的時候,會基於單個元素呼叫Mapper
的方法進行完成通知tryComplete()
,任務完成會最終通知onCompletion()
方法,Reducer
就是在此方法中進行結果的聚合操作。對於流的併發求值來說,過程是類似的,ForEachOp
中最終呼叫ForEachOrderedTask
或者ForEachTask
,這裡挑選ForEachTask
進行分析:
abstract static class ForEachOp<T>
implements TerminalOp<T, Void>, TerminalSink<T, Void> {
// 暫時省略其他程式碼
@Override
public <S> Void evaluateParallel(PipelineHelper<T> helper,
Spliterator<S> spliterator) {
if (ordered)
new ForEachOrderedTask<>(helper, spliterator, this).invoke();
else
// 最終是呼叫ForEachTask的invoke方法,invoke會阻塞到所有fork任務執行完,獲取最終的結果
new ForEachTask<>(helper, spliterator, helper.wrapSink(this)).invoke();
return null;
}
// 暫時省略其他程式碼
}
// ForEachOps類
final class ForEachOps {
private ForEachOps() { }
// forEach的fork-join任務實現,沒有覆蓋getRawResult()方法,最終只會返回NULL
static final class ForEachTask<S, T> extends CountedCompleter<Void> {
// Spliterator例項,如果是父任務則代表所有待處理的元素,如果是子任務則是一個分割後的新Spliterator例項
private Spliterator<S> spliterator;
// Sink鏈例項
private final Sink<S> sink;
// 流管道引用
private final PipelineHelper<T> helper;
// 目標數量,其實是每個任務處理元素數量的建議值
private long targetSize;
// 這個構造器是提供給父(根)任務
ForEachTask(PipelineHelper<T> helper,
Spliterator<S> spliterator,
Sink<S> sink) {
super(null);
this.sink = sink;
this.helper = helper;
this.spliterator = spliterator;
this.targetSize = 0L;
}
// 這個構造器是提供給子任務,所以需要父任務的引用和一個分割後的新Spliterator例項作為引數
ForEachTask(ForEachTask<S, T> parent, Spliterator<S> spliterator) {
super(parent);
this.spliterator = spliterator;
this.sink = parent.sink;
this.targetSize = parent.targetSize;
this.helper = parent.helper;
}
// Similar to AbstractTask but doesn't need to track child tasks
// 實現compute方法,用於分割Spliterator成多個子任務,這裡不需要跟蹤所有子任務
public void compute() {
// 神奇的賦值,相當於Spliterator<S> rightSplit = spliterator; Spliterator<S> leftSplit;
// rightSplit總是指向當前的spliterator例項
Spliterator<S> rightSplit = spliterator, leftSplit;
// 這裡也是神奇的賦值,相當於long sizeEstimate = rightSplit.estimateSize(); long sizeThreshold;
long sizeEstimate = rightSplit.estimateSize(), sizeThreshold;
// sizeThreshold賦值為targetSize
if ((sizeThreshold = targetSize) == 0L)
// 基於Spliterator分割後的右分支例項的元素數量重新賦值sizeThreshold和targetSize
// 計算方式是待處理元素數量/(fork-join執行緒池並行度<<2)或者1(當前一個計算方式結果為0的時候)
targetSize = sizeThreshold = AbstractTask.suggestTargetSize(sizeEstimate);
// 當前的流是否支援SHORT_CIRCUIT,也就是短路特性
boolean isShortCircuit = StreamOpFlag.SHORT_CIRCUIT.isKnown(helper.getStreamAndOpFlags());
// 當前的任務是否fork右分支
boolean forkRight = false;
// taskSink作為Sink的臨時變數
Sink<S> taskSink = sink;
// 當前任務的臨時變數
ForEachTask<S, T> task = this;
// Spliterator分割和建立新的fork任務ForEachTask,前提是不支援短路或者Sink不支援取消
while (!isShortCircuit || !taskSink.cancellationRequested()) {
// 當前的任務中的Spliterator(rightSplit)中的待處理元素小於等於每個任務應該處理的元素閾值或者再分割後得到NULL,則不需要再分割,直接基於rightSplit和Sink鏈執行迴圈處理元素
if (sizeEstimate <= sizeThreshold || (leftSplit = rightSplit.trySplit()) == null) {
// 這裡就是遍歷rightSplit元素回撥Sink鏈的操作
task.helper.copyInto(taskSink, rightSplit);
break;
}
// rightSplit還能分割,則基於分割後的leftSplit和以當前任務作為父任務建立一個新的fork任務
ForEachTask<S, T> leftTask = new ForEachTask<>(task, leftSplit);
// 待處理子任務加1
task.addToPendingCount(1);
// 需要fork的任務例項臨時變數
ForEachTask<S, T> taskToFork;
// 因為rightSplit總是分割Spliterator後對應原來的Spliterator引用,而leftSplit總是trySplit()後生成的新的Spliterator
// 所以這裡leftSplit也需要作為rightSplit進行分割,通俗來說就是周星馳007那把梅花間足發射的槍
if (forkRight) {
// 這裡交換leftSplit為rightSplit,所以forkRight設定為false,下一輪迴圈相當於fork left
forkRight = false;
rightSplit = leftSplit;
taskToFork = task;
// 賦值下一輪的父Task為當前的fork task
task = leftTask;
}
else {
forkRight = true;
taskToFork = leftTask;
}
// 新增fork任務到任務佇列中
taskToFork.fork();
// 其實這裡是更新剩餘待分割的Spliterator中的所有元素數量到sizeEstimate
sizeEstimate = rightSplit.estimateSize();
}
// 置空spliterator例項並且傳播任務完成狀態,等待所有任務執行完成
task.spliterator = null;
task.propagateCompletion();
}
}
}
上面的原始碼分析看起來可能比較難理解,這裡舉個簡單的例子:
public static void main(String[] args) throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream().parallel().forEach(System.out::println);
}
這段程式碼中最終轉換成ForEachTask
中評估後得到的targetSize = sizeThreshold == 1
,當前呼叫執行緒會參與計算,會執行3
次fork
,也就是一共有4
個處理流程例項(也就是原始的Spliterator
例項最終會分割出3
個全新的Spliterator
例項,加上自身一個4
個Spliterator
例項),每個處理流程例項只處理1
個元素,對應的流程圖如下:
最終的計算結果是呼叫CountedCompleter.invoke()
方法獲取的,此方法會阻塞直到所有子任務處理完成,當然forEach
終結操作不需要返回值,所以沒有實現getRawResult()
方法,這裡只是為了阻塞到所有任務執行完畢才解除呼叫執行緒的阻塞狀態。
狀態操作與短路操作
Stream
中按照中間操作是否有狀態可以把這些操作分為無狀態操作和有狀態操作。Stream
中按照終結操作是否支援短路特性可以把這些操作分為非短路操作和短路操作。理解如下:
- 無狀態操作:當前操作節點處理元素完成後,在滿足前提條件下直接把結果傳遞到下一個操作節點,也就是操作內部不存在狀態也不需要儲存狀態,例如
filter
、map
等操作 - 有狀態操作:處理元素的時候,依賴於節點的內部狀態對元素進行累積,當處理一個新的元素的時候,其實可以感知到所有處理過的元素的歷史狀態,這個"狀態"其實更像是緩衝區的概念,例如
sort
、limit
等操作,以sort
操作為例,一般是把所有待處理的元素全部新增到一個容器如ArrayList
,再進行所有元素的排序,然後再重新模擬Spliterator
把元素推送到後一個節點 - 非短路(終結)操作:終結操作在處理元素時候不能基於短路條件提前中斷處理並且返回,也就是必須處理所有的元素,如
forEach
- 短路(終結)操作:終結操作在處理元素時候允許基於短路條件提前中斷處理並且返回,但是最終實現中是有可能遍歷完所有的元素中,只是在處理方法中基於前置的短路條件跳過了實際的處理過程,如
anyMatch
(實際上anyMatch
會遍歷完所有的元素,不過在命中了短路條件下,元素回撥Sink.accept()
方法時候會基於stop
短路標記跳過具體的處理流程)
這裡不展開原始碼進行分析,僅僅展示一個經常見到的Stream
操作彙總表如下:
這裡還有兩點要注意:
- 從原始碼上看部分中間操作也是支援短路的,例如
slice
和while
相關操作 - 從原始碼上看
find
相關終結操作中findFirst
、findAny
均支援和判斷StreamOpFlag.SHORT_CIRCUIT
,而match
相關終結操作是通過內部的臨時狀態stop
和value
進行短路控制
小結
前前後後寫了十多萬字,其實也僅僅相對淺層次介紹了Stream
的基本實現,筆者認為很多沒分析到的中間操作實現和終結操作實現,特別是併發執行的終結操作實現是十分複雜的,多執行緒環境下需要進行一些想象和多處DEBUG
定位執行位置和推演執行的過程。簡單總結一下:
JDK
中Stream
的實現是精煉的高度工程化程式碼Stream
的載體雖然是AbstractPipeline
,管道結構,但是隻用其形,實際求值操作之前會轉化為一個多層包裹的Sink
結構,也就是前文一直說的Sink
鏈,從程式設計模式來看,應用的是Reactor
程式設計模式Stream
目前支援的固有求值執行結構一定是Head(Source Spliterator) -> Op -> Op ... -> Terminal Op
的形式,這算是一個侷限性,沒有辦法做到像LINQ
那樣可以靈活實現類似記憶體檢視的功能Stream
目前支援併發求值方案是針對Source Spliterator
進行分割,封裝Terminal Op
和固定Sink
鏈構造的ForkJoinTask
進行併發計算,呼叫執行緒和fork-join
執行緒池中的工作執行緒都可以參與求值過程,筆者認為這部分是Stream
中除了那些標誌集合位運算外最複雜的實現Stream
實現的功能是一個突破,也有人說過此功能是一個"早產兒",在此希望JDK
能夠在矛盾螺旋中前進和發展
個人部落格原始連結(不定期勘誤):https://throwx.cn/2021/10/06/stream-of-jdk
(s-a-202101005 e-a-20210822 c-14-d)