なぜforEach
はランダムな順序で数値を出力しますが、collect
は並列ストリームからであっても常に元の順序で要素を収集しますか?
Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8};
List<Integer> listOfIntegers = new ArrayList<>(Arrays.asList(intArray));
System.out.println("Parallel Stream: ");
listOfIntegers
.stream()
.parallel()
.forEach(e -> System.out.print(e + " "));
System.out.println();
// Collectors
List<Integer> l = listOfIntegers
.stream()
.parallel()
.collect(Collectors.toList());
System.out.println(l);
出力:
Parallel Stream:
8 1 6 2 7 4 5 3
[1, 2, 3, 4, 5, 6, 7, 8]
ここで行われている「順序付け」には2つの異なる種類があり、議論が混乱します。
1つの種類はencounter orderで、これは streamsのドキュメント で定義されています。これについて考える良い方法は、ソースコレクション内の要素のspatialまたはleft-to-rightの順序です。ソースがList
の場合、前の要素が後の要素の左側にあると考えてください。
processingまたはtemporalの順序もあります。これは、ドキュメントでは定義されていませんが、要素の時間順序です異なるスレッドで処理されます。リストの要素が異なるスレッドによって並列処理されている場合、スレッドはリストの右端の要素を処理してから、左端の要素を処理します。しかし、次回はそうでないかもしれません。
計算が並行して行われる場合でも、ほとんどのCollectors
および一部の端末操作は、ソースから宛先への遭遇順序を独立して保持するように注意深く配置されています。異なるスレッドが各要素を処理する可能性がある時間的順序。
forEach
ターミナル操作は遭遇順序を保存しないことに注意してください。代わりに、次の結果を生成するために発生したスレッドによって実行されます。遭遇順序を維持するforEach
のようなものが必要な場合は、代わりにforEachOrdered
を使用してください。
注文の問題についての詳細は Lambda FAQ も参照してください。
Collectors.toList
method は、返されたCollector
が要素を遭遇順にリストに追加することを指定します。
戻り値:
すべての入力要素を遭遇順にリストに収集するコレクタ
Stream
が並列かどうかは関係ありません。順序は保持されます。
さらに、Collectors
ソースコードを見ると、返されたCollector
は、マージ時にaddAll
でArrayList
を呼び出します。これにより、順序が保持されます。例えば。 1つのスレッドに{1、2}があり、次のスレッドに{3、4}がある場合、addAll
を呼び出すと{1、2、3、4}になります。また、返されたCollector
にはUNORDERED
特性がありません。