2017-05-28 28 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

引用ソースコード行は、2つのストリームではなく** 2つのスプライテータ**で 'estimateSize()言い換えると、*前*で、 'Stream.spliterator()'が呼び出されました。**は**端末操作なので、引用されたドキュメント部分と矛盾していません。ソースストリームが 'ConcatSpliterator'のコンストラクタで破棄されたポイントを超えて怠惰を保存しようとしているのは興味深いですが、これは実装の詳細です。それは影響を正しく伝えていない 'concat'の文書です... – Holger

関連する問題