2017-05-28 184 views
1

當Stream.concat被註釋掉時,createLazyStream按預期工作,僅在應用終端操作時才初始化。Stream.concat錯誤的解決方法

但是,如果你取消註釋Stream.concat線,然後你會看到兩個流進行初始化,即使沒有(沒有應用終端業務)實際使用

這是java的錯誤,對吧? 什麼是解決方法?

@Test 
public void testConcat() { 

    Stream<Integer> stream = createLazyStream(); 
    // Stream<Integer> result = Stream.concat(stream, createLazyStream()); 

} 

private Stream<Integer> createLazyStream() { 
    return StreamSupport.stream(() -> { 

     System.out.println("initializing stream"); 
     return IntStream.range(1,10).spliterator(); 

    }, Spliterator.ORDERED, false); 
} 

回答

6

流不計算,只是拉姆達。這是在ConcatSpliterator構造函數中完成,以測試新的流大小:

unsized = aSpliterator.estimateSize() + bSpliterator.estimateSize() < 0; 

,並呼籲estimateSize,你需要的spliterator,所以它會調用拉姆達得到它。

您可以通過添加Thread.dumpStack()的拉姆達看到這一點:

java.lang.Exception: Stack trace 
    at java.lang.Thread.dumpStack(Thread.java:1333) 
    at test.Main.lambda$0(Main.java:19) 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513) 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536) 
    at java.util.stream.Streams$ConcatSpliterator.<init>(Streams.java:713) 
    at java.util.stream.Streams$ConcatSpliterator$OfRef.<init>(Streams.java:781) 
    at java.util.stream.Stream.concat(Stream.java:1080) 
    at test.Main.main(Main.java:12) 

在現實中,你的測試是有缺陷的。如果您想查看流是否已評估,則可以在返回之前添加peek

return StreamSupport.stream(() -> { 
    return IntStream.range(1,10).peek(i -> System.out.println("peek")).spliterator(); 

}, Spliterator.ORDERED, false); 

現在您會看到當您取消註釋該行時仍然沒有輸出。


看起來好像這是錯誤記錄,雖然(文檔中的'錯誤')。從StreamSupport.stream方法的(Link)的文檔:

的Supplier.get()方法將在供應商被調用的流管道的終端操作開始不超過一次,並且僅後。

+1

_並且僅在流管線的終端操作開始之後_該語句無效。我創建了具有預定義無限估計的延遲分割器,現在它按預期工作,實際流僅在應用終端操作時創建。 – Arthur

+4

引用的源代碼行在兩個**分割器**上調用「estimateSize()」,而不是兩個流。換句話說,在*之前,'Stream.spliterator()'已經被調用,其中**是一個終端操作,所以它與引用的文檔部分並不矛盾。有趣的是,源流嘗試保留超出了ConcatSpliterator構造函數中銷燬的點的懶惰,但這些是實現細節。這是'concat'的文檔,它不能正確說明影響...... – Holger