回答:
図のような単純なケースでは、ほとんど同じです。ただし、重要である可能性がある微妙な違いがいくつかあります。
1つの問題は注文にあります。ではStream.forEach
、順序がされて未定義。シーケンシャルストリームで発生する可能性は低いですが、それでも、Stream.forEach
任意の順序で実行することは仕様の範囲内です。これは、並列ストリームで頻繁に発生します。対照的に、Iterable.forEach
はIterable
、指定されている場合、常にの反復順序で実行されます。
別の問題は副作用にあります。で指定されてStream.forEach
いるアクションは、非干渉である必要があります。(java.util.streamパッケージdocを参照してください。)Iterable.forEach
制限が少ない可能性があります。内のコレクションのためにjava.util
、Iterable.forEach
一般的にそのコレクションの使用されますIterator
れるように設計されているほとんどが、高速の失敗とスローされますどのConcurrentModificationException
コレクションは構造的に反復中に変更された場合を。しかし、構造的でない変更がされている反復中に許可されます。たとえば、ArrayListクラスのドキュメントには、「単に要素の値を設定するだけでは構造が変更されない」と記載されています。したがって、ArrayList.forEach
ArrayList
問題なく基になる値を設定できます。
並行コレクションはまた異なります。フェイルファストではなく、一貫性が弱いように設計されています。完全な定義はそのリンクにあります。ただし、簡単に説明しますConcurrentLinkedDeque
。そのforEach
メソッドに渡されたアクションは、構造的にも、基になる両端キューを変更することが許可されており、ConcurrentModificationException
スローされることはありません。ただし、発生する変更は、この反復で表示される場合と表示されない場合があります。(したがって、「弱い」一貫性です。)
Iterable.forEach
同期されたコレクションを繰り返し処理する場合、さらに別の違いが表示されます。そのようなコレクションでIterable.forEach
は、コレクションのロックを一度取得し、アクションメソッドへのすべての呼び出しにわたってロックを保持します。Stream.forEach
コールはロックしないコレクションのspliteratorを使用し、非干渉の現行ルールに依存しています。ストリームをバッキングするコレクションは、反復中に変更される可能性があり、変更されている場合は、ConcurrentModificationException
動作または一貫性のない動作が発生する可能性があります。
ArrayList
、同時変更をかなり厳密にチェックするため、しばしばスローされConcurrentModificationException
ます。ただし、これは特に並列ストリームの場合は保証されません。CMEの代わりに、予期しない答えが返される場合があります。ストリームソースの非構造的な変更も検討してください。並列ストリームの場合、特定の要素を処理するスレッドや、変更時に処理されているかどうかはわかりません。これにより、実行ごとに異なる結果が得られ、CMEが得られない可能性のある競合状態が設定されます。
この答えは、ループのさまざまな実装のパフォーマンスに関係しています。VERY OFTENと呼ばれるループにわずかに関連します(数百万の呼び出しのように)。ほとんどの場合、ループの内容は断然最も高価な要素です。本当に頻繁にループする状況では、これはまだ興味深いかもしれません。
これは実装固有であるため、完全なソースコードであるため、ターゲットシステムでこのテストを繰り返す必要があります。
高速Linuxマシンでopenjdkバージョン1.8.0_111を実行しています。
integers
(10 ^ 0-> 10 ^ 5エントリ)のさまざまなサイズのこのコードを使用して、リストを10 ^ 6回ループするテストを作成しました。
結果は以下のとおりです。最速の方法は、リスト内のエントリの数によって異なります。
ただし、依然として最悪の状況では、10 ^ 5エントリを10 ^ 6回ループすると、最悪のパフォーマーでは100秒かかりました。そのため、事実上すべての状況で他の考慮事項がより重要になります。
public int outside = 0;
private void forCounter(List<Integer> integers) {
for(int ii = 0; ii < integers.size(); ii++) {
Integer next = integers.get(ii);
outside = next*next;
}
}
private void forEach(List<Integer> integers) {
for(Integer next : integers) {
outside = next * next;
}
}
private void iteratorForEach(List<Integer> integers) {
integers.forEach((ii) -> {
outside = ii*ii;
});
}
private void iteratorStream(List<Integer> integers) {
integers.stream().forEach((ii) -> {
outside = ii*ii;
});
}
これが私のタイミングです:ミリ秒/関数/リスト内のエントリ数。各実行は10 ^ 6ループです。
1 10 100 1000 10000
iterator.forEach 27 116 959 8832 88958
for:each 53 171 1262 11164 111005
for with index 39 112 920 8577 89212
iterable.stream.forEach 255 324 1030 8519 88419
実験を繰り返す場合は、完全なソースコードを投稿しました。この回答を編集して、テストしたシステムの表記で結果を追加してください。
MacBook Pro、2.5 GHz Intel Core i7、16 GB、macOS 10.12.6を使用:
1 10 100 1000 10000
iterator.forEach 27 106 1047 8516 88044
for:each 46 143 1182 10548 101925
for with index 49 145 887 7614 81130
iterable.stream.forEach 393 397 1108 8908 88361
Java 8 Hotspot VM-3.4 GHz Intel Xeon、8 GB、Windows 10 Pro
1 10 100 1000 10000
iterator.forEach 30 115 928 8384 85911
for:each 40 125 1166 10804 108006
for with index 30 120 956 8247 81116
iterable.stream.forEach 260 237 1020 8401 84883
Java 11 Hotspot VM-3.4 GHz Intel Xeon、8 GB、Windows 10 Pro
(上記と同じマシン、異なるJDKバージョン)
1 10 100 1000 10000
iterator.forEach 20 104 940 8350 88918
for:each 50 140 991 8497 89873
for with index 37 140 945 8646 90402
iterable.stream.forEach 200 270 1054 8558 87449
Java 11 OpenJ9 VM-3.4 GHz Intel Xeon、8 GB、Windows 10 Pro
(上記と同じマシンとJDKバージョン、異なるVM)
1 10 100 1000 10000
iterator.forEach 211 475 3499 33631 336108
for:each 200 375 2793 27249 272590
for with index 384 467 2718 26036 261408
iterable.stream.forEach 515 714 3096 26320 262786
Java 8ホットスポットVM-2.8 GHz AMD、64 GB、Windows Server 2016
1 10 100 1000 10000
iterator.forEach 95 192 2076 19269 198519
for:each 157 224 2492 25466 248494
for with index 140 368 2084 22294 207092
iterable.stream.forEach 946 687 2206 21697 238457
Java 11 Hotspot VM-2.8 GHz AMD、64 GB、Windows Server 2016
(上記と同じマシン、異なるJDKバージョン)
1 10 100 1000 10000
iterator.forEach 72 269 1972 23157 229445
for:each 192 376 2114 24389 233544
for with index 165 424 2123 20853 220356
iterable.stream.forEach 921 660 2194 23840 204817
Java 11 OpenJ9 VM-2.8 GHz AMD、64 GB、Windows Server 2016
(上記と同じマシンとJDKバージョン、異なるVM)
1 10 100 1000 10000
iterator.forEach 592 914 7232 59062 529497
for:each 477 1576 14706 129724 1190001
for with index 893 838 7265 74045 842927
iterable.stream.forEach 1359 1782 11869 104427 958584
選択したVMの実装によっても、ホットスポット/ OpenJ9などが異なります。
あなたが言及した2つに違いはありません。少なくとも概念的には、これCollection.forEach()
は単なる速記です。
内部的には、stream()
バージョンにはオブジェクトの作成のために多少多くのオーバーヘッドがありますが、実行時間を見るとどちらにもオーバーヘッドはありません。
両方の実装では、繰り返し処理を終了しcollection
、一度コンテンツ、及び中素子アウト反復プリント。
Stream
作成されているのか、それとも個々のオブジェクトのことですか?申し訳ありStream
ませんが、a は要素を複製しません。
Collection.forEach()は、コレクションのイテレータ(指定されている場合)を使用します。つまり、アイテムの処理順序が定義されます。対照的に、Collection.stream()。forEach()の処理順序は定義されていません。
ほとんどの場合、どちらを選択しても違いはありません。並列ストリームを使用すると、複数のスレッドでストリームを実行できます。このような状況では、実行順序は定義されていません。Javaでは、Collectors.toList()などの端末操作が呼び出される前に、すべてのスレッドが終了することだけが必要です。最初にコレクションで直接forEach()を呼び出し、次に並列ストリームで呼び出す例を見てみましょう。
list.forEach(System.out::print);
System.out.print(" ");
list.parallelStream().forEach(System.out::print);
コードを複数回実行すると、list.forEach()が挿入順にアイテムを処理するのに対し、list.parallelStream()。forEach()は実行ごとに異なる結果を生成します。可能な出力の1つは次のとおりです。
ABCD CDBA
もう一つは:
ABCD DBCA
Iterable.forEach takes the collection's lock
。この情報はどこから来ましたか?JDKソースでそのような動作を見つけることができません。