Gibt es einen Unterschied in der Leistung zwischen einem for-Schleife und einem foreach-Schleife?

StackOverflow https://stackoverflow.com/questions/256859

  •  05-07-2019
  •  | 
  •  

Frage

Was, wenn überhaupt, ist der Leistungsunterschied zwischen den folgenden zwei Schleifen?

for (Object o: objectArrayList) {
    o.DoSomething();
}

und

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}
War es hilfreich?

Lösung

Von Punkt 46 in Effective Java von Joshua Bloch:

  

Die für-jede Schleife, eingeführt in   Release 1.5, wird von der Unordnung loswerden   und die Möglichkeit für Fehler durch   Ausblenden der Iterator oder Indexvariable   vollständig. Die sich ergebende Idiom   gilt gleichermaßen für Sammlungen und   Arrays:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}
     

Wenn Sie den Doppelpunkt sehen (:), lesen Sie es als   „In“. So oberhalb der Schleife liest als   „Für jedes Element in e-Elementen.“ Note   dass es keine Leistungseinbuße   für die for-Schleife unter Verwendung von jedem, auch für   Arrays. In der Tat kann es eine leichte bieten   Leistungsvorteil gegenüber einem gewöhnlichen   unter bestimmten Umständen für die Schleife, wie es   berechnet die Grenze des Array-Index   nur einmal. Während Sie können dies tun, indem   Hand (Punkt 45), Programmierer nicht   immer tun.

Andere Tipps

All diese Schleifen tun genau das gleiche, ich will nur diese zeigen, bevor sie in meine zwei Cent zu werfen.

Zuerst wird der klassische Weg durch Liste der Looping:

for(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }

Zweitens ist die bevorzugte Art und Weise, da es weniger fehleranfällig ist (wie oft haben Sie das getan „oops, gemischt, um die Variablen i und j in diesen Schleifen innerhalb von Schleifen“ Dinge?).

for(String s : strings) { /* do something using s */ }

Drittens, das Mikro-optimiert für die Schleife:

int size = strings.size();
for(int i=0;++i<=size;) { /* do something using strings.get(i) */ }

Jetzt ist der tatsächliche zwei Cent: Zumindest, wenn ich diese teste, die dritten die schnellsten waren beim Zählen Millisekunden ab, wie lange es mit einem einfachen Vorgang für jede Art von Schleife nahm darin ein paar Millionen Mal wiederholt - das war Verwendung von Java 5 mit jre1.6u10 unter Windows, falls jemand interessiert ist.

Während es bei scheint mindestens so zu sein, dass die dritte der schnellste ist, sollten Sie sich wirklich fragen, ob Sie das Risiko der Durchführung dieses Guckloch-Optimierung überall in Ihrem Looping Code nehmen wollen, da von dem, was ich gesehen habe, tatsächlich Looping ist nicht in der Regel die meiste Zeit ein Teil von jeder realen Programm raubend (oder vielleicht bin ich nur arbeiten auf dem falschen Feld, wer weiß). Und wie auch erwähnte ich in dem Vorwand für das Java foreach-Schleife (einige darauf verweisen, wie Iterator Schleife und andere als for-in-Schleife ) Sie sind weniger wahrscheinlich, dass eine bestimmte dumme Fehler zu treffen, wenn es zu benutzen. Und bevor diskutieren, wie diese sogar noch schneller als die andere sein kann, denken Sie daran, dass javac optimiert nicht Bytecode überhaupt (na ja, fast überhaupt sowieso), kompiliert es nur es.

Wenn Sie in Mikro-Optimierung obwohl und / oder Software verwendet viele der rekursiven Schleifen und so dann können Sie in der dritten Schleife Art interessiert. Denken Sie daran, Benchmark-Software auch vor und nach der for-Schleifen zu ändern Sie müssen diese ungerade, Mikro-optimierte ein.

Die für-jede Schleife im allgemeinen bevorzugt werden soll. Der „get“ -Ansatz kann langsamer sein, wenn die Liste Implementierung Sie verwenden nicht Random Access unterstützt. Wenn zum Beispiel ein LinkedList verwendet wird, würden Sie eine Traversal Kosten entstehen, während die foreach-Ansatz verwendet einen Iterator, den Überblick über ihre Position in der Liste hält. Mehr Informationen über die Nuancen der für-jede Schleife .

Ich denke, der Artikel ist jetzt hier: neuer Standort

Der Link hier gezeigt tot war.

Nun, Auswirkungen auf die Leistung meist unbedeutend, aber nicht Null ist. Wenn Sie auf JavaDoc von RandomAccess Schnittstelle aussehen:

  

Als Faustregel gilt: eine Liste   Umsetzung sollte dies implementieren   Schnittstelle, wenn für typische Fälle von   die Klasse, diese Schleife:

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);
     

läuft schneller als diese Schleife:

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();

Und für-jede Schleife wird mit der Version mit Iterator, so für ArrayList zB for-each-Schleife ist nicht am schnellsten.

Es scheint leider einen Unterschied zu sein.

Wenn Sie sich die erzeugten Bytes Code suchen für beide Arten von Schleifen, sie sind anders.

Hier ist ein Beispiel aus dem Log4j-Quellcode.

In /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java wir haben eine statische innere Klasse namens Log4jMarker, die definiert:

    /*
     * Called from add while synchronized.
     */
    private static boolean contains(final Marker parent, final Marker... localParents) {
        //noinspection ForLoopReplaceableByForEach
        for (final Marker marker : localParents) {
            if (marker == parent) {
                return true;
            }
        }
        return false;
    }

Mit Standard-Schleife:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: iconst_0
       1: istore_2
       2: aload_1
       3: arraylength
       4: istore_3
       5: iload_2
       6: iload_3
       7: if_icmpge     29
      10: aload_1
      11: iload_2
      12: aaload
      13: astore        4
      15: aload         4
      17: aload_0
      18: if_acmpne     23
      21: iconst_1
      22: ireturn
      23: iinc          2, 1
      26: goto          5
      29: iconst_0
      30: ireturn

Mit for-each:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: aload_1
       1: astore_2
       2: aload_2
       3: arraylength
       4: istore_3
       5: iconst_0
       6: istore        4
       8: iload         4
      10: iload_3
      11: if_icmpge     34
      14: aload_2
      15: iload         4
      17: aaload
      18: astore        5
      20: aload         5
      22: aload_0
      23: if_acmpne     28
      26: iconst_1
      27: ireturn
      28: iinc          4, 1
      31: goto          8
      34: iconst_0
      35: ireturn

Was ist los mit diesem Oracle?

Ich habe versucht, dies mit Java 7 und 8 unter Windows 7.

Es ist immer besser, den Iterator statt Indizierung zu verwenden. Dies liegt daran, Iterator wird höchstwahrscheinlich für die Liste Implementierung während indiziert (Aufruf get) optimzied möglicherweise nicht. LinkedList Zum Beispiel ist eine Liste, aber die Indizierung durch die Elemente wird langsamer sein als Iterieren des Iterator.

foreach macht die Absicht des Codes klarer und wird in der Regel über eine sehr geringe Verbesserung der Geschwindigkeit bevorzugt -. Wenn überhaupt

Jedes Mal, wenn ich eine indizierte Schleife sehen, ich habe es zu analysieren, ein wenig länger sicherzustellen, dass es das tut, was ich denkt es tut Z.B. Ist es von Null startet, wird es schließen oder den Endpunkt usw. ausschließen.?

Die meiste Zeit scheint Code ausgegeben wird das Lesen (die ich schrieb, oder jemand schrieb anderes) und Klarheit ist fast immer wichtiger als die Leistung. Es ist einfach, in diesen Tagen entlassen Leistung, weil Hotspot so eine tolle Arbeit leistet.

Mit dem folgenden Code:

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

interface Function<T> {
    long perform(T parameter, long x);
}

class MyArray<T> {

    T[] array;
    long x;

    public MyArray(int size, Class<T> type, long x) {
        array = (T[]) Array.newInstance(type, size);
        this.x = x;
    }

    public void forEach(Function<T> function) {
        for (T element : array) {
            x = function.perform(element, x);
        }
    }
}

class Compute {
    int factor;
    final long constant;

    public Compute(int factor, long constant) {
        this.factor = factor;
        this.constant = constant;
    }

    public long compute(long parameter, long x) {
        return x * factor + parameter + constant;
    }
}

public class Main {

    public static void main(String[] args) {
        List<Long> numbers = new ArrayList<Long>(50000000);
        for (int i = 0; i < 50000000; i++) {
            numbers.add(i * i + 5L);
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.size(); i++) {
            x += x * 7 + numbers.get(i) + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        numbers = null;
        MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return x * 8 + parameter + 5L;
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
        myArray = null;
        myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return new Compute(8, 5).compute(parameter, x);
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
    }
}

Gibt folgende Ausgabe auf meinem System:

224
-699150247503735895
221
-699150247503735895
220
-699150247503735895
219
-699150247503735895

Ich bin mit Ubuntu 12.10 Alpha mit OracleJDK 1.7 Update 6.

Generell HotSpot optimiert viele Umwege und einfache reduntant Operationen, so dass im Allgemeinen sollten Sie nicht darum kümmern, es sei denn es gibt eine Menge von ihnen in seqence sind oder sie sind stark verschachtelte.

Auf der anderen Seite, indexiert auf LinkedList ist viel langsamer als nächstes auf Iterator für LinkedList Aufruf, so dass Sie, dass Leistungseinbußen vermeiden kann, während die Lesbarkeit zu halten, wenn Sie Iteratoren verwenden (explizit oder implizit in foreach-Schleife).

Auch mit so etwas wie einer Arraylist oder Vektor, wo „get“ ein einfacher Array-Lookup ist, hat die zweite Schleife noch zusätzlichen Aufwand, dass die ersten nicht. Ich würde erwarten, dass es ein kleines bisschen langsamer als die erste sein.

Der einzige Weg, um sicher zu wissen ist es Benchmark, und auch das ist nicht so einfach wie es klingen kann. Der JIT-Compiler kann, um Ihren Code sehr unerwartete Dinge tun.

Hier ist eine kurze Analyse der Differenz von dem Android-Entwicklungsteam löscht:

https://www.youtube.com/watch?v=MZOf3pOAM6A

Das Ergebnis ist, dass es ist ein Unterschied, und in sehr zurückhaltenden Umgebungen mit sehr großen Listen könnte es ein spürbarer Unterschied. In ihrer Prüfung für jede Schleife, die dauerte doppelt so lang. Allerdings war ihre Prüfung über eine Arraylist von 400.000 Zahlen. Der eigentliche Unterschied pro Element in der Anordnung betrug 6 Mikrosekunden . Ich habe nicht geprüft und sie nicht sagen, aber ich würde erwarten, dass die Differenz etwas größer sein, Objekte mit eher als Primitive, aber auch nach wie vor, wenn Sie Bibliothekscode bauen, wo Sie haben keine Ahnung, das Ausmaß dessen, was Sie aufgefordert werden, iterieren, ich glaube, der Unterschied ist nicht wert, betont etwa.

Mit dem Variablennamen objectArrayList, gehe ich davon aus, dass eine Instanz von java.util.ArrayList ist. In diesem Fall würde der Performance-Unterschied nicht bemerkbar sein.

Auf der anderen Seite, wenn es eine Instanz von java.util.LinkedList ist, wird der zweite Ansatz viel langsamer als die List#get(int) ein O (n) -Operation ist.

Der erste Ansatz ist immer bevorzugt, wenn der Index durch die Logik in der Schleife benötigt wird.

1. for(Object o: objectArrayList){
    o.DoSomthing();
}
and

2. for(int i=0; i<objectArrayList.size(); i++){
    objectArrayList.get(i).DoSomthing();
}

Sowohl tut die gleichen, aber für einfache und sichere Programmierung Verwendung for-each, gibt es Möglichkeiten für Fehler anfällig in der 2. Art und Weise zu verwenden.

Es ist seltsam, dass niemand die offensichtliche erwähnt - foreach Speicher (in Form eines Iterators) zuordnet, wohingegen ein normaler for-Schleife kein Speicher zuordnet. Für Spiele auf Android, ist dies ein Problem, weil es bedeutet, dass der Garbage Collector periodisch ausgeführt wird. In einem Spiel, wollen Sie nicht die Garbage Collector läuft ... je. Also nicht foreach-Schleifen in Ihrem Draw (oder machen) -Methode.

akzeptierte Antwort beantwortet die Frage, abgesehen von Ausnahmefall Arraylist ...

Da die meisten Entwickler setzen auf Arraylist (atleast Ich glaube so)

So bin ich verpflichtet hier die richtige Antwort hinzuzufügen.

Gerade aus der Entwicklerdokumentation: -

Die for-Schleife verbessert (manchmal auch als „for-each“ Schleife genannt) kann für Sammlungen verwendet werden, die Iterable Schnittstelle und für Arrays implementieren. Mit Sammlungen wird ein Iterator zugeordnet Schnittstelle Anrufe hasNext () und next () zu machen. Mit einer Arraylist, eine handgeschriebene gezählt Schleife ist ca. 3x schneller (mit oder ohne JIT), sondern auch für andere Sammlungen der for-Schleife Syntax erweitert wird genau entsprechen explizite Iterator Nutzung.

Es gibt mehrere Alternativen für die durch ein Array iterieren:

static class Foo {
    int mSplat;
}

Foo[] mArray = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < mArray.length; ++i) {
        sum += mArray[i].mSplat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].mSplat;
    }
}

public void two() {
    int sum = 0;
    for (Foo a : mArray) {
        sum += a.mSplat;
    }
}

Null () am langsamsten ist, da der JIT kann die Kosten des Erhaltens die Array-Länge einmal für jede Iteration durch die Schleife noch nicht optimiert weg.

ein () ist schneller. Es zieht alles aus in die lokalen Variablen, die Lookups zu vermeiden. Nur die Array-Länge bietet einen Leistungsvorteil.

zwei () ist am schnellsten für Geräte ohne JIT, und nicht von einem () für Geräte mit JIT. Es verwendet die for-Schleife Syntax in der Version 1.5 des Java-Programmiersprache eingeführt verbessert.

Also, sollten Sie die erweiterte for-Schleife standardmäßig verwenden, aber eine handschriftliche gezählt Schleife für leistungskritische Arraylist Iteration berücksichtigen.

public class FirstJavaProgram {

    public static void main(String[] args) 
    {
        int a[]={1,2,3,45,6,6};

// Method 1: this is simple way to print array 

        for(int i=0;i<a.length;i++) 
        { 
            System.out.print(a[i]+" ");
        }

// Method 2: Enhanced For loop

        for(int i:a)
        {
            System.out.print(i+" ");
        }
    }
}

Ja, for-each Variante ist schneller als als normal index-based-for-loop.

for-each Variante verwendet iterator. So ist Verfahrgeschwindigkeit schneller als normal for Schleife, der Index basiert.
Dies liegt daran, iterator werden zum Verfahren optimiert, da es auf zeigt kurz vor dem nächsten Element und kurz nach dem vorherigen Element . Einer der Gründe für index-based-for-loop langsam zu sein, ist, dass, es haben, berechnen und jedes Mal auf das Elemente Position bewegen , die nicht mit dem iterator ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top