學習Java8的Stream

  Stream把對一個集合的很多操作,以一種流式作業串起來,按照類似函數式編程書寫代碼,界面清爽清晰。
  Stream不同於Guava的FluentIterable系列。FluentIterable系列,是通過封裝集合(List、Set等)並且重載迭代器、get的方式,進行的transform、filter等,優點是簡單並且性能高。缺點是功能單一、並且容易誤用。比如,對transform之後的列表的每個項,本質上都是一個視圖(View),而不是實際的項(值對象)。多次調用get方法。這本質上每次都會層層調用Function的apply方法,如果其中有複雜運算或者I/O讀取,效率是非常低的。
  Stream本身,建立了一系列的數據結構,其中的數據都可以看成是實際存在的數據,而並不僅僅是視圖。網上分析源代碼的文章非常多,這裏不重複描述,而是僅僅介紹理解的一個思路。
  首先,是幾個關鍵的數據結構。
  Stream。接口是BaseStream,可以看做是流基本『形態』的描述。或者說,每個流式作業。雖然後面提到的Pipeline也是實現了BaseStream,但是這樣似乎更容易理解。
  Pipeline。公共父類是AbstractPipeline。可以看做是流式作業中的每個作業,或者說是每個作業節點。爲了防止不必要的裝箱和拆箱操作,又分成了ReferencePipeline、IntPipeline、LongPipeline、DoublePipeline。這些管道可以分成三種:頭、終結操作、非終結操作。分別對應的類是XXXXPipeline.Head、XXXXPipeline.StatelessOp和XXXXPipeline.StatefulOp、TerminalOp.調用流的工作方法,都會製造出來這樣一個Pipeline。比如,調用IntStream.of(),就會生成一個頭;調用Stream.map(),會生成一個非終結操作;調用Stream.reduce(),生成一個終結操作。非終結操作裏面,都要實現opWrapSink方法,該方法要返回一個Sink。
  Sink。每個操作,對應一個Sink。每個Sink,關注三個方法:begin、end、accept。如果當前的Sink沒有操作,那麼直接調用downstream.accept;否則,把當前操作結果作爲參數調用downstream.accept。downstream是什麼概念呢?比如,一個流的操作是IntStream.of(1,2,3).map(x -> x + 1).boxed().max()。那麼map對應的sink,就是頭的downstream,boxed對應的sink就是map的downstream。綜合起來,只要調用了頭的accept,就會層層調用到最後一個終結操作。終結操作沒有opWrapSink方法,所以自然不會調用到後續的流。
  Spliterator。流裏面數據的訪問工具。如果是串行流,一般是直接調用裏面的forEachRemaining。該方法裏關注action.accept,如果之前串聯好了每個Sink,那麼這裏一句調用,就開始了Sink的層層調用。
  至此,基本數據結構就介紹這些。下面,關注流的每個節點,是如何連起來的。
  每個Stream,由一組Pipeline節點組成,每追加一個操作,都會向這組Pipeline後面追加一個Pipeline結構。追加時候維護的信息裏面,關注sourceStage(頭),previousStage(上一個Pipeline),sourceSupplier(流數據的Spliterator),depth(Pipeline長度)。一直到最後的終結操作,連城一個Pipeline鏈。
  對於終結操作,不論是reduce,還是collect操作,都會調用到AbstracePipeline.evaluate方法。以串行流的reduce爲例,直接調用到AbstractPipeline.wrapAndCopyInto。
  其中,wrap是遍歷Pipeline鏈,調用每個階段的opWrapSink。這樣每個Sink通過方法逐層調用(而非內存數據上的指針鏈接),從第一個Pipeline的Sink鏈接到末尾。

    @Override
    @SuppressWarnings("unchecked")
    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;
    }

  copyInto是依次調用Sink.begin,Spliterator.forEachRemaing,Sink.end方法。上文曾經提到,forEachRemaing會以流裏的每項數據爲參數,層層調用每級Sink的accept。

    @Override
    final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
        Objects.requireNonNull(wrappedSink);

        if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
            wrappedSink.begin(spliterator.getExactSizeIfKnown());
            spliterator.forEachRemaining(wrappedSink);
            wrappedSink.end();
        }
        else {
            copyIntoWithCancel(wrappedSink, spliterator);
        }
    }

  至此,整個流的操作,自上而下完全聯繫到了一起。
  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章