web-dev-qa-db-ja.com

拡張forループが通常のforループよりも効率的である理由

ここで、for for loopは通常のfor loopよりも効率的であると読みました:

http://developer.Android.com/guide/practices/performance.html#foreach

それらの効率の違いを検索したとき、私が見つけたのはすべてです:通常のforループの場合、配列の長さやサイズなどを見つけるために追加のステップが必要です。

for(Integer i : list){
   ....
}


int n = list.size();
for(int i=0; i < n; ++i){
  ....
}

しかし、これが唯一の理由ですか?拡張forループは通常のforループよりも優れていますか?その場合、拡張されたforループを理解するのはわずかに複雑であるため、通常のforループを使用してください。

興味深い問題については、これを確認してください: http://www.coderanch.com/t/258147/Java-programmer-SCJP/certification/Enhanced-Loop-Vs-Loop

これら2つのタイプのforループの内部実装について説明したり、拡張forループを使用する他の理由を説明してください

26
Archie.bpgc

拡張forループの方が効率的であると言うのは少し単純化しすぎです。それはcan beですが、多くの場合、古い学校のループとほとんど同じです。

最初に注意することは、コレクションの場合、拡張forループはIteratorを使用するため、Iteratorを使用してコレクションを手動で反復する場合、拡張とほぼ同じパフォーマンスが得られることです。 forループ。

拡張forループが単純に実装従来のループよりも高速な場所の1つは、次のようなものです。

LinkedList<Object> list = ...;

// Loop 1:
int size = list.size();
for (int i = 0; i<size; i++) {
   Object o = list.get(i);
   /// do stuff
}

// Loop 2:
for (Object o : list) {
  // do stuff
}

// Loop 3:
Iterator<Object> it = list.iterator();
while (it.hasNext()) {
  Object o = it.next();
  // do stuff
}

この場合、ループ1はループ2とループ3の両方よりも遅くなります。これは、位置iで要素を見つけるために各反復で(部分的に)リストを走査する必要があるためです。ただし、ループ2および3は、Iteratorを使用しているため、リスト内でさらに1つの要素のみをステップ実行します。ループ2と3のパフォーマンスもほぼ同じです。ループ3は、ループ2でコードを記述するときにコンパイラが生成するものとほぼ同じであるためです。

51
Joachim Sauer

上記の点で今日行った小さな実験に私は驚いています。だから、私はリンクリストに特定の数の要素を挿入し、上記の3つの方法を使用してそれを反復した1)アドバンストforループを使用する2)イテレータを使用する3)シンプルなループとget()を使用する
あなたのようなプログラマーは、コードを見ることで私がしたことをよりよく理解できると思います。

long advanced_timeElapsed,iterating_timeElapsed,simple_timeElapsed;
    long first=System.nanoTime();
    for(Integer i: myList){
        Integer b=i;
    }
    long end= System.nanoTime();
    advanced_timeElapsed=end-first;
    System.out.println("Time for Advanced for loop:"+advanced_timeElapsed);
    first=System.nanoTime();
    Iterator<Integer> it = myList.iterator();
    while(it.hasNext())
    {
        Integer b=it.next();
    }
    end= System.nanoTime();
    iterating_timeElapsed=end-first;
    System.out.println("Time for Iterating Loop:"+iterating_timeElapsed);
    first=System.nanoTime();
    int counter=0;
    int size= myList.size();
    while(counter<size)
    {
        Integer b=myList.get(counter);
        counter++;  
    }
    end= System.nanoTime();
    simple_timeElapsed=end-first;
    System.out.println("Time for Simple Loop:"+simple_timeElapsed);

私が期待したものではない結果。以下は、3つのケースで経過した時間のグラフです。 Time Elapsed Graph

Y軸時間経過X軸テストケース
テストケース1:10の入力
テストケース2:30の入力
テストケース3:50の入力
テストケース4:100入力
テストケース5:150の入力
テストケース6:300入力
テストケース7:500の入力
テストケース8:1000入力
テストケース9:2000の入力
テストケース10:5000の入力
テストケース11:10000の入力
テストケース12:100000の入力

ここで、上記のコードでエラーが見つかった場合、単純なループが他のループよりもパフォーマンスが優れていることがわかります。バイトコードを掘り下げた後、これについてさらに更新し、内部で何が起こっているのかを確認します。このように長い反応をおbutび申し上げますが、説明が好きです。フィリップ

8
Philip George

拡張forループは、通常のforループよりも効率的だと読みました。

実際には、プログラムの効率が低下することもありますが、ほとんどの場合はまったく同じです。

開発者にとってより効率的であり、多くの場合、はるかに重要です

For-eachループは、コレクションを反復処理するときに特に役立ちます。

List<String> list = 
for(Iterator<String> iter = list.iterator(); list.hasNext(); ) {
    String s = list.next();

次のように簡単に記述できます(ただし、同じことを行うため、プログラムにとっては効率的ではありません)

List<String> list = 
for(String s: list) {

インデックスによってランダムにアクセス可能なコレクションにアクセスする場合、「古い」ループを使用する方がわずかに効率的です。

List<String> list = new ArrayList<String>(); // implements RandomAccess
for(int i=0, len = list.size(); i < len; i++) // doesn't use an Iterator!

コレクションでfor-eachループを使用すると、ランダムアクセスリストの効率がやや劣るIteratorが常に使用されます。

知る限り、for-eachループを使用することはプログラムにとって決して効率的ではありませんが、先ほど言ったように、開発者の効率はしばしばはるかに重要です。

7
Peter Lawrey

For-eachはIteratorインターフェースを使用します。 「古い」スタイルよりも効率的であるとは思わない。イテレータは、リストのサイズも確認する必要があります。

それは主に読みやすさのためです。

LinkedListのような非ランダムアクセスコレクションの方が高速ですが、比較は不公平です。とにかく、(インデックス化されたアクセスが遅い)2番目の実装に慣れていなかっただろう。

3
Thilo

Foreachループは、この種のループと同じくらい効率的です。

for (Iterator<Foo> it = list.iterator(); it.hasNext(); ) {
     Foo foo = it.next();
     ...
}

厳密に同等だからです。

を使用してリストを反復処理する場合

int size = list.size();
for (int i = 0; i < size; i++) {
    Foo foo = list.get(i);
    ...
}

次に、foreachループは、ループと同等のパフォーマンスを持ちますただし、ArrayListのみ。 LinkedListの場合、ループはひどいパフォーマンスになります。各反復で、ith要素に到達するまでリストのすべてのノードを走査する必要があるためです。

イテレーターは現在のノードへの参照を保持し、各反復で次のノードに進むため、foreachループ(または同じイテレーターに基づくループ)にはこの問題はありません。これは、すべてのタイプのリストで正常に機能するため、最も優れた選択肢です。また、意図をより明確に表現し、ループ内でインデックスをインクリメントしたり、ネストされたループの場合に間違ったインデックスを使用したりするリスクがないため、より安全です。

3
JB Nizet

すべての回答は良好であり、これ以上の回答は必要ないと思いますが、次のことを指摘したいだけです。

拡張forステートメントは、obtain array elementsに対してonlyのみを使用できます

itcannotbe notusedto modify elements

プログラムで要素を変更する必要がある場合は、従来の counter-controlledforステートメントを使用します。

ご覧ください

int[] array = { 1, 2, 3, 4, 5 };
for (int counter = 0; counter < array.length; counter++)
    array[counter] *= 2;

enhanced forであるため、modifying the array’s elementsステートメントを使用できませんでした。

0

Effective Javaから:

配列をループするための標準的なイディオムでは、必ずしも冗長なチェックが行われるとは限りません。最新のJVM実装はそれらを最適化します。

しかし、Josh Blochは、JVMがそれらを最適化する方法を説明していません。

0
Geek