web-dev-qa-db-ja.com

ストリームおよび遅延評価

私はストリームの抽象化に関する Java 8 API を読んでいますが、この文はあまりよくわかりません。

中間操作は新しいストリームを返します。それらは常に怠け者です。 filter()などの中間操作を実行しても、実際にはフィルタリングは実行されませんが、代わりに、指定された述語に一致する初期ストリームの要素を含む新しいストリームが作成されます。パイプラインソースのトラバースは、パイプラインのターミナル操作が実行されるまで開始されません。

フィルター操作が新しいストリームを作成するとき、そのストリームにはフィルターされた要素が含まれていますか?ストリームが要素を含むのは、それが端末操作でトラバースされたときだけであることを理解しているようです。しかし、それよりも、フィルタリングされたストリームには何が含まれていますか?よくわかりません!!!

33
xdevel2000

これは、フィルターが端末操作中にのみ適用されることを意味します。次のようなものを考えてください:

public Stream filter(Predicate p) {
    this.filter = p; // just store it, don't apply it yet
    return this; // in reality: return a new stream
}
public List collect() {
    for (Object o : stream) {
        if (filter.test(o)) list.add(o);
    }
    return list;
}

(それはコンパイルされず、現実の単純化ですが、原則はそこにあります)

51
assylias

端末操作が呼び出されない限り、中間操作は評価されないため、ストリームは遅延します。

各中間操作は新しいストリームを作成し、提供された操作/関数を保存し、新しいストリームを返します。

パイプラインは、これらの新しく作成されたストリームを蓄積します。

端末操作が呼び出され、ストリームのトラバースが開始され、関連する機能が1つずつ実行される時間。

パラレルストリームは、ストリームを1つずつ(終点で)評価しません。使用可能なコアに応じて、操作はむしろ同時に実行されます。

13
Nisarg Patil

私には、その中間操作は正確に怠zyではないようです:

List<String> l3 = new ArrayList<String>();
        l3.add("first");
        l3.add("second");
        l3.add("third");
        l3.add("fouth");
        l3.add("fith");
        l3.add("sixth");

        List<String> test3 = new ArrayList<String>();
        try {
            l3.stream().filter(s -> { l3.clear(); test3.add(s); return true;}).forEach(System.out::println);
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("!!! ");
            System.out.println(test3.stream().reduce((s1, s2) -> s1 += " ;" + s2).get());
        }

出力:

  first
    null
    null
    null
    null
    null
    Java.util.ConcurrentModificationException
        at Java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.Java:1380)
        at Java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.Java:481)
        at Java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.Java:471)
        at Java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.Java:151)
        at Java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.Java:174)
        at Java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.Java:234)
        at Java.util.stream.ReferencePipeline.forEach(ReferencePipeline.Java:418)
        at test.TestParallel.main(TestParallel.Java:69)
    !!! 

    first ;null ;null ;null ;null ;null

ストリーム作成時の反復セットの数のように見えますが、新しいストリーム要素が遅延します。

カウンター付きループと比較してください:

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>(); 
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    int i = 0;
    while (i < list.size()) {
        System.out.println(list.get(i++));
        list.clear();
    }
}

出力:

1

予想される反復は1つだけです。私はストリームで例外をスローする動作の問題に同意しますが、怠zyはデータを取得する(または何らかのアクションを実行する)ことを意味します。データの数もデータです。