填坑
先來填坑,不知大家還記得我在第五章《串行與并行》中留的坑嗎?下面我們就來繼續挖它,通過剖析源碼,一層一層撥開它的心。
萬流之眼 StreamSupport輔助類
為什么只是將parallel標志位設為false或true就可以關閉或開啟并行,真正的實現原理是什么呢?我們先來看看集合類的stream方法與parallelStream方法有什么不同,結果可能令人啞然失笑。
//Collection接口中的默認實現
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
這兩個方法都是像我們之前使用Spliterator的時候那樣,直接調用了StreamSupport的stream方法,區別只在于最后的并行標志位。類似的代碼也出現在了Stream類的of、iterate、generate等方法中,由此可以推斷,StreamSupport的stream方法就是是系統類庫中產生流的唯一方法,當然這里說的流不包括基本類型流。
追本溯源 Pipeline世家
StreamSupport的stream方法返回了一個ReferencePipeline.Head對象。ReferencePipeline是個抽象類,繼承了AbstractPipeline,并實現了其中大多數的方法,而Head是ReferencePipeline的內部類,繼承了ReferencePipeline并實現了剩下的兩個方法。總結一下就是Head內部類繼承了ReferencePipeline抽象類繼承了AbstractPipeline抽象類實現了Stream接口,所以Head就是我們最后得到的Stream。
最終實現 Head內部類
Head中對ReferencePipeline留下的兩個沒有實現的方法的實現看起來很古怪,都是直接拋出了<em style="color:#FF0000;">UnsupportedOperationException</em>,表示不支持該操作。我們也不用關注這些,而是把注意力放在它重寫的另外兩個方法上,名字大家都很熟悉,forEach和forEachOrdered。這兩個方法的重寫也很簡單,以forEach為例:
if (!isParallel()) {
sourceStageSpliterator().forEachRemaining(action);
}
else {
super.forEach(action);
}
該方法僅僅只是判斷一下isParallel,如果是并行的就還是調父類的方法,否則就調用Spliterator的forEachRemaining方法,該方法在Spliterator接口中有默認的實現,只有一行代碼:
do { } while (tryAdvance(action));
是不是很絕,什么也不做,等到tryAdvance返回false就結束。tryAdvance我們前面已經講到,是用來判斷分割式迭代是否結束的。也就是說,如果流是串行的,就不會進行分割,這時候Spliterator就變成了普通的迭代器,從頭到尾逐個進行操作。
命令執行 TerminalOp眾子
對于并行流,Head則會調用父類ReferencePipeline中的同名方法,該方法中又會調用evaluate方法,根據不同的行為如forEach、reduce來選擇執行不同的操作。所有的操作類都要實現TerminalOp接口,如forEach操作就對應著ForEachOp類,reduce操作對應著ReduceOp類,這些操作會作為參數傳給evaluate方法。evaluate方法最后還是會根據isParallel來選擇執行并行操作還是串行操作,兩類操作分別對應著需要重寫的evaluateParallel方法與evaluateSequentia方法(evaluateParallel有默認實現),兩個方法均由操作類來實現,并且都需要傳遞一個Spliterator參數。
幕后核心 Spliterator接口
不管并行流還是串行流,函數調用的過程中,都無一例外的需要傳遞一個Spliterator參數。第一次看到這里的時候我覺得很玄,很多時候系統給你提供個類,你不去用,到頭來看源碼卻發現發現原來它才是萬物起源。這后面的事情,百川歸海,我就不一一細說了,有興趣的讀者可以自行探索。
先遛了
學個屁Java8,哪有多少人用,還總結,總結個屁。我先跑去學Kotlin了,這玩意可比Java8爽多了……