1. ホーム

[解決済み】Collection.stream().forEach()とCollection.forEach()の違いは何ですか?)

2022-03-29 23:34:35

質問

というのはわかるのですが .stream() のようなチェーンオペレーションを使用することができます。 .filter() または並列ストリームを使用します。しかし、小さな操作(例えば、リストの要素を表示する)を実行する必要がある場合、両者の違いは何でしょうか?

collection.stream().forEach(System.out::println);
collection.forEach(System.out::println);

解決方法は?

図のような単純なケースであれば、ほとんど同じです。しかし、微妙な違いがいくつもあり、それが重要な意味を持つ場合もあります。

一つは、順序の問題である。この場合 Stream.forEach の場合、順序は 未定義 . シーケンシャルなストリームで発生する可能性は低いですが、それでも、以下の仕様の範囲内です。 Stream.forEach を任意の順序で実行することができます。これは並列ストリームでは頻繁に発生します。それに対して Iterable.forEach の繰り返し順序で常に実行されます。 Iterable が指定されている場合は、その限りではない。

もう一つの問題は、副作用の問題です。で指定されたアクションは Stream.forEach が必要です。 非干渉型 . (参照 java.util.streamパッケージドキュメント .) Iterable.forEach の方が制約が少ない可能性があります。のコレクションについては java.util , Iterable.forEach は通常、そのコレクションの Iterator のように設計されています。 フェイルファスト を投げ、その結果 ConcurrentModificationException は、反復処理中にコレクションが構造的に変更された場合に発生します。しかし、構造的でない変更は は、イテレーション中に許可される。例えば ArrayListクラスのドキュメント には、「単に要素の値を設定することは、構造的な変更ではない」とあります。 ArrayList.forEach の値を設定することが許されています。 ArrayList を問題なく実行できます。

同時進行のコレクションはまた別物です。フェイルファストではなく、次のように設計されています。 弱い整合性 . 完全な定義はリンク先にあります。簡単にですが、次のように考えてみましょう。 ConcurrentLinkedDeque . その forEach メソッド は、構造的にでも基礎となる deque を修正することが許されています。 ConcurrentModificationException が投げられることはありません。しかし、発生した変更は、この繰り返しで見えるかもしれないし、見えないかもしれない。(それゆえ、"weak" の一貫性がある。)

さらにもう一つの違いは Iterable.forEach は同期化されたコレクションに対して反復処理を行います。そのようなコレクションに対して Iterable.forEach コレクションをロックする を一度だけ実行し、アクションメソッドのすべての呼び出しに渡ってそれを保持します。そのため Stream.forEach の呼び出しは、コレクションのスプリテレータを使用します。スプリテレータはロックをせず、一般的な不干渉のルールに従います。ストリームをバックアップしているコレクションは、反復処理中に変更される可能性があり、その場合は ConcurrentModificationException や矛盾した動作になる可能性があります。