常用的流操作
在深入原理之前,我們有必要知道關(guān)于Stream的一些基礎(chǔ)知識,關(guān)于Stream的操作分類,如表1-1所示。
表1-1 Stream的常用操作分類(表格引自這里)
如表1-1中所示,Stream中的操作可以分為兩大類:中間操作與結(jié)束操作,中間操作只是對操作進行了記錄,只有結(jié)束操作才會觸發(fā)實際的計算(即惰性求值),這也是Stream在迭代大集合時高效的原因之一。中間操作又可以分為無狀態(tài)(Stateless)操作與有狀態(tài)(Stateful)操作,前者是指元素的處理不受之前元素的影響;后者是指該操作只有拿到所有元素之后才能繼續(xù)下去。結(jié)束操作又可以分為短路與非短路操作,這個應該很好理解,前者是指遇到某些符合條件的元素就可以得到最終結(jié)果;而后者是指必須處理所有元素才能得到最終結(jié)果。
源碼解讀
最基礎(chǔ)類對應有ReferencePipeline以及終結(jié)操作對應的ops(例如forEach對應的是ForEachOps),請求有大體分為兩種SHORT_CIRCUIT和非SHORT_CIRCUIT,非SHORT_CIRCUIT表示請求會依次得到sink處理,SHORT_CIRCUIT表示請求可能會中斷,只處理其中某些因子。對于鏈式處理會將所有的鏈依次生成對應的sink鏈,后續(xù)會介紹
打個比方stream1.filter(predicate1).filter(predicate2).forEach(consumer1);
首先會調(diào)用ReferencePipeline的filter,生成StatelessOp的op,主要可以關(guān)注opWrapSink方法,然再次調(diào)用filter生成職責鏈,通過previousStage進行串接,類似于生成了opForPredicate2 ->opForPredicate1,然后調(diào)用終結(jié)函數(shù)forEach,通過ForEachOps生成ForEachOp的終結(jié)sink,然后調(diào)用FroEachOp的evaluateSequential進行請求處理,然后就是訪問者模式相互訪問來去,看源碼的時候關(guān)心點放在如下圖:
wrapSink就是為了將最后的consumer通過前面介紹的sinkChain進行包裝,生成opForPredicate1Sink->opForPredicate2Sink->consumerAction.
然后通過是否為short_circuit進行不同處理,非short_circuit表示所有的屬性都要進行操作,否則表示要進行篩選終結(jié)。
非short_circuit如下:
比如數(shù)組的,獲取每個數(shù)組元素,進行action.accept,其中accept就是上面講解的sinkChain.
short_circuit如下:
以limit為例子,先電泳forEachWithCancel,首先判斷sink是或否需要取消請求,sliceOp對應的opWrapSink會維護一個m,每次處理一個請求就會減一。當為0 的時候就結(jié)束循環(huán)。
最后,對于stream1.filter(predicate1).limit(1).filter(predicate2).forEach(consumer1);
解析為:生成predicate1Sink->sliceOpSink(m=1)->predicate2Sink->consumer1Action,
依次遍歷stream1中的元素(stream1對應的spliterator的tryAdvance方法),然后判斷sinkChain是否需要cancelRequest(predicate1->sliceOpSink->predicate2Sink),如果需要處理,則調(diào)用spliterator的tryAdvance執(zhí)行sinkChain