java8ストリームでの処理の順序を確実にする方法は?


148

XMLJavaオブジェクト内でリストを処理したい。受け取った順にすべての要素を処理する必要があります。

したがってsequentialstream私は使用するそれぞれを呼び出す必要がありますか? list.stream().sequential().filter().forEach()

または、並列処理を使用しない限り、ストリームを使用するだけで十分ですか? list.stream().filter().forEach()

回答:


338

あなたは間違った質問をしています。あなたは約求めているsequentialparallelあなたがアイテムを処理するのに対し、順番にあなたが尋ねるする必要がありますので、発注順序付けられたストリームがあり、順序を維持することを保証する操作を実行する場合、ストリームが並列で処理されるか順次で処理されるかは関係ありません。実装は順序を維持します。

順序付けされたプロパティは、並列と順次とは異なります。たとえば、ストリームを呼び出すstream()HashSet、ストリームは順序付けられず、呼び出しstream()は順序付けられたストリームをList返します。呼び出しunordered()て注文契約を解除すると、パフォーマンスが向上する可能性があることに注意してください。ストリームに順序がない場合、順序を再確立する方法はありません。(順序付けられていないストリームを順序付けられたものに変える唯一の方法は、を呼び出すことsortedですが、結果の順序は必ずしも元の順序とは限りません)。

参照してください。「順序」セクションjava.util.streamパッケージのドキュメントを

ストリーム操作全体を通して順序を確実に維持するには、ストリームのソース、すべての中間操作、およびターミナル操作のドキュメントを調べて、それらが順序を維持するかどうか(またはソースに最初の順序があるかどうか)を調べる必要があります。場所)。

これは非常に微妙な場合があります。たとえばStream.iterate(T,UnaryOperator)、順序付けられたストリームをStream.generate(Supplier)作成しながら、順序付けられていないストリームを作成します。順序を維持できないため、質問でもよくある間違いを犯したことに注意してください。ストリームの要素を保証された順序で処理する場合に使用する必要があります。forEach forEachOrdered

したがってlist、質問の内容が本当にであるjava.util.List場合、そのstream()メソッドは順序付けられたストリームを返し、順序をfilter変更しません。したがって、を呼び出すとlist.stream().filter() .forEachOrdered()、すべての要素が順番にlist.parallelStream().filter().forEachOrdered()処理されますが、要素の場合は(フィルターなどによって)並列に処理される可能性がありますが、ターミナルアクションは順番に呼び出されます(並列実行の利点が明らかに低下します)。 。

たとえば、次のような操作を使用する場合

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

操作全体で並列実行のメリットが得られる可能性がありますが、並列ストリームとシーケンシャルストリームのどちらを使用するかに関係なく、結果のリストは常に正しい順序になります。


48
はい、いい答えです。私が見つけた1つのことは、「前」、「後」など、少なくとも英語で使用する用語が非常にあいまいであることです。ここには2種類の順序があります。1)遭遇順序空間順序とも呼ばれます)、および2)処理順序時間順序とも呼ばれます)。この違いを念頭に置いて、エンカウンターの順序を説明するときは「左」または「右」などの単語を使用し、処理順序を説明するときは「より前」または「より後」という言葉を使用すると便利です。
スチュアートマークス

私は理解してList<>順序を維持しますが、うCollection<>
ジョシュ

5
@JoshC。実際のコレクションタイプによって異なります。Sets SortedSetまたはでない限り、通常はありませんLinkedHashSet。コレクションビューMapkeySet()entrySet()、およびvalues()継承)Mapマップがあるときのポリシーは、つまりは、順序付けられSortedMapたりLinkedHashMap。動作は、コレクションのスプリッターで報告される特性によって決まります。のdefault実装は特性をCollection報告しないORDEREDため、オーバーライドされない限り、順序付けされていません。
Holger

@ホルガー私はあなたの答えの小さなセクションに多少関連しているかもしれない質問がありました。
ナマン

1
注目に値するのは、並列ストリームを使用forEachOrderedするforEach場合とは異なるだけです。ただし、蒸し方が変わる場合に備えて、順序付けが重要な場合に使用することをお勧めします...
Steve Chambers

0

一言で言えば:

順序は、ソースデータ構造と中間ストリーム操作に依存します。あなたがList処理を使用していると仮定すると、処理を注文する必要があります(filterここではシーケンスを変更しないため)。

詳細:

順次vs並列vs順不同:

Javadocs

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

ストリームの順序:

Javadocs

ストリームには、定義されたエンカウンター順序がある場合とない場合があります。ストリームに遭遇順序があるかどうかは、ソースと中間操作に依存します。特定のストリームソース(Listや配列など)は本質的に順序付けられていますが、その他(HashSetなど)は順序付けされていません。Sorted()などの一部の中間操作では、その他の順序付けられていないストリームに遭遇順序を課すことがあり、その他の操作では、BaseStream.unordered()などの順序付けされたストリームを順序付けせずにレンダリングすることがあります。さらに、一部の端末操作では、forEach()などの遭遇順序が無視される場合があります。

ストリームが順序付けられている場合、ほとんどの操作は遭遇順に要素を操作するように制約されます。ストリームのソースが[1、2、3]を含むリストである場合、map(x-> x * 2)の実行結果は[2、4、6]でなければなりません。ただし、ソースに定義された遭遇順序がない場合、値[2、4、6]の順列は有効な結果になります。

順次ストリームの場合、エンカウンター順序の有無はパフォーマンスには影響せず、決定論にのみ影響します。ストリームが順序付けられている場合、同じソースで同じストリームパイプラインを繰り返し実行すると、同じ結果が生成されます。順序付けされていない場合、繰り返し実行すると異なる結果が生じる可能性があります。

並列ストリームの場合、順序付けの制約を緩和すると、より効率的な実行が可能になる場合があります。重複のフィルタリング(distinct())やグループ化された削減(Collectors.groupingBy())などの特定の集約操作は、要素の順序が関係ない場合により効率的に実装できます。同様に、limit()など、本質的にエンカウンターの順序に関連付けられている操作では、適切な順序を保証するためにバッファリングが必要になる場合があり、並列処理の利点が損なわれます。ストリームに出会い順があるが、ユーザーがその出会い順を特に気にしない場合、unordered()を使用してストリームを明示的に並べ替えると、一部のステートフルまたはターミナル操作の並列パフォーマンスが向上する可能性があります。ただし、上記の「ブロックの重みの合計」の例など、ほとんどのストリームパイプラインは、

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.