web-dev-qa-db-ja.com

For-each vsイテレータ。どちらが良いオプションでしょう

次のシナリオを検討してください。

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
}
86
Tala

違いは、イテレータが反復中のコレクションからアイテムを削除できることを除いて、主に構文上の砂糖です。技術的には、forループの拡張により、少なくともコレクションと配列の両方を含むIterableをループ処理できます。

パフォーマンスの違いを心配しないでください。このようなミクロ最適化は、無関係な注意散漫です。進行中にアイテムを削除する必要がある場合は、イテレーターを使用します。そうでない場合、forループは読みやすいという理由だけで使用される傾向があります。

for (String s : stringList) { ... }

対:

for (Iterator<String> iter = stringList.iterator(); iter.hasNext(); ) {
  String s = iter.next();
  ...
}
16

_for-each_は高度なループ構造です。内部的にはIteratorを作成し、Collectionを反復処理します。 _for-each_構造に対して実際のIteratorオブジェクトを使用することの唯一の利点は、.remove()などのIteratorのメソッドを使用してコレクションを変更できることです。反復中にIteratorのメソッドを使用せずにコレクションを変更すると、 ConcurrentModificationException。 が生成されます

9
Aniket Thakur
3

簡単な答え:いいえ、いいえ。

内部的にfor-eachループはIteratorを作成して、コレクションを反復処理します。

Iteratorを明示的に使用する利点は、Iteratorsメソッドにアクセスできることです。

2
Uwe Plonus

リスト内のアイテムを置き換えたい場合は、forループを使用して古い学校に行きます

for (int nIndex=0; nIndex < list.size(); nIndex++) {
  Obj obj = (Obj) list.get(nIndex);

  // update list item
  list.set(nIndex, obj2);
}
0
Bob

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を構成しましたが、それが理由ではないかもしれません。

enter image description here

0
Birchlabs

これは、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_パフォーマンスはその中間に位置します。

0
Rahul Raina