次のシナリオを検討してください。
List<String> list = new ArrayList<>();
次に、このリストにString
値を追加しました。
リスト内の各要素を次の方法で移動しました。
オプションone- use for-each
for (String i : list) {
System.out.println(i);
}
オプション2-Iterator
を使用
Iterator it=list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
Iterator
の代わりにfor-each
を使用すると、パフォーマンス上の利点があることを知りたいだけです。また、Javaで数日後にIteratorを使用するのは悪い習慣ですか?
for-each
は、iterators
(アプローチ2)を使用するための構文糖衣です。
ループ内のコレクションを変更する必要がある場合は、iterators
を使用する必要があります。最初のアプローチは例外をスローします。
for (String i : list) {
System.out.println(i);
list.remove(i); // throws exception
}
Iterator it=list.iterator();
while (it.hasNext()){
System.out.println(it.next());
it.remove(); // valid here
}
違いは、イテレータが反復中のコレクションからアイテムを削除できることを除いて、主に構文上の砂糖です。技術的には、forループの拡張により、少なくともコレクションと配列の両方を含むIterableをループ処理できます。
パフォーマンスの違いを心配しないでください。このようなミクロ最適化は、無関係な注意散漫です。進行中にアイテムを削除する必要がある場合は、イテレーターを使用します。そうでない場合、forループは読みやすいという理由だけで使用される傾向があります。
for (String s : stringList) { ... }
対:
for (Iterator<String> iter = stringList.iterator(); iter.hasNext(); ) {
String s = iter.next();
...
}
_for-each
_は高度なループ構造です。内部的にはIteratorを作成し、Collectionを反復処理します。 _for-each
_構造に対して実際のIteratorオブジェクトを使用することの唯一の利点は、.remove()
などのIteratorのメソッドを使用してコレクションを変更できることです。反復中にIteratorのメソッドを使用せずにコレクションを変更すると、 ConcurrentModificationException。 が生成されます
これを行う最良の方法は、Java 8 is、
list.forEach(System.out::println);
便利なリンクをいくつか紹介します。
簡単な答え:いいえ、いいえ。
内部的にfor-each
ループはIterator
を作成して、コレクションを反復処理します。
Iterator
を明示的に使用する利点は、Iterator
sメソッドにアクセスできることです。
リスト内のアイテムを置き換えたい場合は、forループを使用して古い学校に行きます
for (int nIndex=0; nIndex < list.size(); nIndex++) {
Obj obj = (Obj) list.get(nIndex);
// update list item
list.set(nIndex, obj2);
}
foreach
はとにかく内部でイテレーターを使用します。それは本当に単なる構文糖です。
次のプログラムを検討してください。
_import Java.util.List;
import Java.util.ArrayList;
public class Whatever {
private final List<Integer> list = new ArrayList<>();
public void main() {
for(Integer i : list) {
}
}
}
_
_javac Whatever.Java
_でコンパイルしましょう。
そして_javap -c Whatever
_を使用して、main()
の逆アセンブルされたバイトコードを読み取ります。
_public void main();
Code:
0: aload_0
1: getfield #4 // Field list:Ljava/util/List;
4: invokeinterface #5, 1 // InterfaceMethod Java/util/List.iterator:()Ljava/util/Iterator;
9: astore_1
10: aload_1
11: invokeinterface #6, 1 // InterfaceMethod Java/util/Iterator.hasNext:()Z
16: ifeq 32
19: aload_1
20: invokeinterface #7, 1 // InterfaceMethod Java/util/Iterator.next:()Ljava/lang/Object;
25: checkcast #8 // class Java/lang/Integer
28: astore_2
29: goto 10
32: return
_
foreach
が以下のプログラムにコンパイルされることがわかります。
List.iterator()
を使用してイテレータを作成しますIterator.hasNext()
の場合:Iterator.next()
を呼び出してループを続行します「この無駄なループがコンパイルされたコードから最適化されないのはなぜですか?リストアイテムで何もしないことがわかります」:.iterator()
には副作用があります。つまり、.hasNext()
には副作用や意味のある結果があります。
データベースからのスクロール可能なクエリを表すiterableが.hasNext()
で劇的な何かをするかもしれないと簡単に想像できます(データベースに接続したり、結果セットの終わりに達したためにカーソルを閉じたりします)。
したがって、ループ本体で何も起こらないことを証明できたとしても、反復しても意味のある/結果が何も起こらないことを証明する方が高価です(扱いにくい?)。コンパイラーは、この空のループ本体をプログラムに残しなければなりません。
期待できる最善のものは、コンパイラwarningです。 _javac -Xlint:all Whatever.Java
_がnotでこの空のループ本体について警告するのは興味深いことです。 IntelliJ IDEAはそうします。確かにEclipseコンパイラを使用するようにIntelliJを構成しましたが、それが理由ではないかもしれません。
これは、Javaで実行される_For-each
_のトラバーサルの_ArrayList<String>
_ vs Iterator
vs for
のパフォーマンスをチェックする簡単なコードスニペットです。バージョン8。
_ long MAX = 2000000;
ArrayList<String> list = new ArrayList<>();
for (long i = 0; i < MAX; i++) {
list.add("" + i);
}
/**
* Checking with for each iteration.
*/
long A = System.currentTimeMillis();
for (String data : list) {
// System.out.println(data);
}
long B = System.currentTimeMillis();
System.out.println(B - A + "ms");
/**
* Checking with Iterator method
*/
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
// System.out.println(iterator.next());
}
long C = System.currentTimeMillis();
System.out.println(C - B + "ms");
/**
* Checking with normal iteration.
*/
for (int i = 0; i < MAX; i++) {
list.get((int) (i % (MAX - i)));
// System.out.println(list.get(i));
}
long D = System.currentTimeMillis();
System.out.println(D - C + "ms");
_
平均出力値:
_19ms
9ms
27ms
_
結果分析:
Iterator
(9ms)<_For-each
_(19ms)<For
(27ms)ここで
Iterator
は最高のパフォーマンスを発揮しますおよびFor
は最低のパフォーマンスを発揮します。ただし、_For-each
_パフォーマンスはその中間に位置します。