Java 8 Stream 原理 - 調試筆記

Java 8 Stream 原理——調試筆記

 

理解的前提:對流的執行步驟有基本認識——中間操作和終止操作

 

示例程序:

Stream<String> stream = Stream.of( "a", "bdd", "bdd" );

stream.filter( s -> s.length() > 1 )//

        .sorted()//

        .mapToInt( String::length )//

        .sum();

 

代碼解析:

1) stream.filter(xx); // 對應的是ReferencePipeline 類,創建一個新的流,將當前流賦值給新的流 previousStage 字段,並返回新創建的流。

代碼:

return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE ...

2) sorted()mapToInt() 方法都類似。

3) xx.sum() 對應的是IntPipeline

4) 代碼產生流的鏈結構

sum() = mapToInt(pre) -> sorted(pre) -> filter(pre) -> stream

 

由於流只有調用終止操作纔會真正執行,所以在 .sum() 打斷點,開始調試。。。

 

斷點調試

xx.sum(); // F5 進入 sum() 方法

reduce(0, Integer::sum); // F5 進入

ReduceOps.makeInt(identity, op) // 產生一個終止符 TerminalOp

evaluate(ReduceOps.makeInt(identity, op)); // F5, F7, F5 進入到 AbstractPipeline 

sourceSpliterator(terminalOp.getOpFlags()) // 創建一個 spliterator (暫叫分叉器)

terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags())); // F5, F7, F5, F7, F5 進入到 ReduceOps

helper.wrapAndCopyInto(makeSink(), spliterator).get();

// 1) F5, F7, F5 進入 AbstractPipeline.wrapAndCopyInto(S, Spliterator<P_IN>)

// 2) F5, F7, F5 進入 wrapSink(Sink<E_OUT>) // 此方法返回一個調用鏈爲 filter() -> sorted() -> mapToInt() 收集點(sink)

// 3) F7, F5 進入 copyInto(Sink<P_IN>, Spliterator<P_IN>)

// 3a) wrappedSink.begin(spliterator.getExactSizeIfKnown()); 算是真正開始執行流式操作

// 4) F5 spliterator.forEachRemaining(wrappedSink) 進入 Spliterators$ArraySpliterator 類,此方法是對集合元素進行迭代。

 

action 對應的是 filter()

再看 ReferencePipeline.filter(xx) 源碼

                    public void accept(P_OUT u) {

                        if (predicate.test(u)) // 測試通過

                            downstream.accept(u); // 下一個流處理

                }

 

下一個流對應的是 sorted()

跟蹤 ReferencePipeline.sorted() -> SortedOps.makeRef(this) -> new OfRef<>(upstream)

comparator 是一個默認的比較器:Comparator.naturalOrder()

SortedOps$OfRef.opWrapSink(int flags, Sink<T> sink) 方法創建 RefSortingSink 對象。

具體看 RefSortingSink

也可以在 spliterator.forEachRemaining(wrappedSink) 裏的

do { action.accept((T)a[i]); } while (++i < hi); 一直 F5, F7 也能找到

RefSortingSink 的工作方式很簡單:accept(T t) 只是添加元素;end() 方法才排序,然後 for 循環調用 downstream.accept(t),最後調用 downstream.end();

其中 RefSortingSink.end() 是在 AbstractPipeline.copyInto(Sink<P_IN>, Spliterator<P_IN>) 裏調用的(代碼:wrappedSink.end();

 

下一個流對應的是 mapToInt()

查看 ReferencePipeline.mapToInt(xx),很簡單——將值轉換成 int 然後傳給 downstream

 

最後sum() 調用的是 TerminalOp.get(),裏面的 state operator.applyAsInt(state, t) 累加的

 

 

算法的理解

    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;

}

 

sum() = mapToInt(pre) -> sorted(pre) -> filter(pre) -> stream

 

sum(none) -> map(prev) -> sort()

sink = sum(none).opWrapSink(none)

 

sink = sum(sink) // this = sum

sink = map(sum(sink)) // map = sum.prev, sink = sum(sink)

sink = sort(map(sum(sink))) // sort = map.prev, sink = map(sum(sink))

靈感來了一下就能理解,靈感沒來糾結了半天也不能理解能用畫圖理解更好.

教訓:super(upstream, opFlags); 沒再跟進去,導致字段理解有誤!!!

 

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