填坑
先來填坑芹血,不知大家還記得我在第五章《串行與并行》中留的坑嗎?下面我們就來繼續(xù)挖它啃擦,通過剖析源碼饿悬,一層一層撥開它的心。
萬流之眼 StreamSupport輔助類
為什么只是將parallel標(biāo)志位設(shè)為false或true就可以關(guān)閉或開啟并行珠叔,真正的實(shí)現(xiàn)原理是什么呢弟劲?我們先來看看集合類的stream方法與parallelStream方法有什么不同兔乞,結(jié)果可能令人啞然失笑凉唐。
//Collection接口中的默認(rèn)實(shí)現(xiàn)
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
這兩個(gè)方法都是像我們之前使用Spliterator的時(shí)候那樣霍骄,直接調(diào)用了StreamSupport的stream方法读整,區(qū)別只在于最后的并行標(biāo)志位。類似的代碼也出現(xiàn)在了Stream類的of米间、iterate车伞、generate等方法中,由此可以推斷困曙,StreamSupport的stream方法就是是系統(tǒng)類庫(kù)中產(chǎn)生流的唯一方法谦去,當(dāng)然這里說的流不包括基本類型流。
追本溯源 Pipeline世家
StreamSupport的stream方法返回了一個(gè)ReferencePipeline.Head對(duì)象要糊。ReferencePipeline是個(gè)抽象類妆丘,繼承了AbstractPipeline勺拣,并實(shí)現(xiàn)了其中大多數(shù)的方法,而Head是ReferencePipeline的內(nèi)部類药有,繼承了ReferencePipeline并實(shí)現(xiàn)了剩下的兩個(gè)方法愤惰。總結(jié)一下就是Head內(nèi)部類繼承了ReferencePipeline抽象類繼承了AbstractPipeline抽象類實(shí)現(xiàn)了Stream接口扇单,所以Head就是我們最后得到的Stream奠旺。
最終實(shí)現(xiàn) Head內(nèi)部類
Head中對(duì)ReferencePipeline留下的兩個(gè)沒有實(shí)現(xiàn)的方法的實(shí)現(xiàn)看起來很古怪阻桅,都是直接拋出了<em style="color:#FF0000;">UnsupportedOperationException</em>,表示不支持該操作。我們也不用關(guān)注這些稽寒,而是把注意力放在它重寫的另外兩個(gè)方法上杏糙,名字大家都很熟悉,forEach和forEachOrdered赖淤。這兩個(gè)方法的重寫也很簡(jiǎn)單谅河,以forEach為例:
if (!isParallel()) {
sourceStageSpliterator().forEachRemaining(action);
}
else {
super.forEach(action);
}
該方法僅僅只是判斷一下isParallel,如果是并行的就還是調(diào)父類的方法吐限,否則就調(diào)用Spliterator的forEachRemaining方法褂始,該方法在Spliterator接口中有默認(rèn)的實(shí)現(xiàn)崎苗,只有一行代碼:
do { } while (tryAdvance(action));
是不是很絕,什么也不做胆数,等到tryAdvance返回false就結(jié)束幅慌。tryAdvance我們前面已經(jīng)講到,是用來判斷分割式迭代是否結(jié)束的胰伍。也就是說骂租,如果流是串行的,就不會(huì)進(jìn)行分割但汞,這時(shí)候Spliterator就變成了普通的迭代器,從頭到尾逐個(gè)進(jìn)行操作僵缺。
命令執(zhí)行 TerminalOp眾子
對(duì)于并行流踩叭,Head則會(huì)調(diào)用父類ReferencePipeline中的同名方法,該方法中又會(huì)調(diào)用evaluate方法自脯,根據(jù)不同的行為如forEach斤富、reduce來選擇執(zhí)行不同的操作。所有的操作類都要實(shí)現(xiàn)TerminalOp接口满力,如forEach操作就對(duì)應(yīng)著ForEachOp類,reduce操作對(duì)應(yīng)著ReduceOp類,這些操作會(huì)作為參數(shù)傳給evaluate方法焕参。evaluate方法最后還是會(huì)根據(jù)isParallel來選擇執(zhí)行并行操作還是串行操作,兩類操作分別對(duì)應(yīng)著需要重寫的evaluateParallel方法與evaluateSequentia方法(evaluateParallel有默認(rèn)實(shí)現(xiàn))脚囊,兩個(gè)方法均由操作類來實(shí)現(xiàn)龟糕,并且都需要傳遞一個(gè)Spliterator參數(shù)。
幕后核心 Spliterator接口
不管并行流還是串行流悔耘,函數(shù)調(diào)用的過程中讲岁,都無一例外的需要傳遞一個(gè)Spliterator參數(shù)。第一次看到這里的時(shí)候我覺得很玄衬以,很多時(shí)候系統(tǒng)給你提供個(gè)類缓艳,你不去用,到頭來看源碼卻發(fā)現(xiàn)發(fā)現(xiàn)原來它才是萬物起源看峻。這后面的事情,百川歸海互妓,我就不一一細(xì)說了溪窒,有興趣的讀者可以自行探索。
先遛了
學(xué)個(gè)屁Java8冯勉,哪有多少人用澈蚌,還總結(jié),總結(jié)個(gè)屁灼狰。我先跑去學(xué)Kotlin了宛瞄,這玩意可比Java8爽多了……