web-dev-qa-db-ja.com

Java 8で並列ストリームが順次収集される理由

なぜ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]
34
Barat Sahdzijeu

ここで行われている「順序付け」には2つの異なる種類があり、議論が混乱します。

1つの種類はencounter orderで、これは streamsのドキュメント で定義されています。これについて考える良い方法は、ソースコレクション内の要素のspatialまたはleft-to-rightの順序です。ソースがListの場合、前の要素が後の要素の左側にあると考えてください。

processingまたはtemporalの順序もあります。これは、ドキュメントでは定義されていませんが、要素の時間順序です異なるスレッドで処理されます。リストの要素が異なるスレッドによって並列処理されている場合、スレッドはリストの右端の要素を処理してから、左端の要素を処理します。しかし、次回はそうでないかもしれません。

計算が並行して行われる場合でも、ほとんどのCollectorsおよび一部の端末操作は、ソースから宛先への遭遇順序を独立して保持するように注意深く配置されています。異なるスレッドが各要素を処理する可能性がある時間的順序

forEachターミナル操作は遭遇順序を保存しないことに注意してください。代わりに、次の結果を生成するために発生したスレッドによって実行されます。遭遇順序を維持するforEachのようなものが必要な場合は、代わりにforEachOrderedを使用してください。

注文の問題についての詳細は Lambda FAQ も参照してください。

44
Stuart Marks

Collectors.toList method は、返されたCollectorが要素を遭遇順にリストに追加することを指定します。

戻り値:

すべての入力要素を遭遇順にリストに収集するコレクタ

Streamが並列かどうかは関係ありません。順序は保持されます。

さらに、Collectorsソースコードを見ると、返されたCollectorは、マージ時にaddAllArrayListを呼び出します。これにより、順序が保持されます。例えば。 1つのスレッドに{1、2}があり、次のスレッドに{3、4}がある場合、addAllを呼び出すと{1、2、3、4}になります。また、返されたCollectorにはUNORDERED特性がありません。

10
rgettman