1729 Stimmen

Wie funktioniert die Java-'for each'-Schleife?

Bedenken Sie:

List<String> someList = new ArrayList<String>();
// add "monkey", "donkey", "skeleton key" to someList

for (String item : someList) {
    System.out.println(item);
}

Was wäre das Äquivalent for Schleife aussehen, ohne die Verwendung der für jede Syntax?

1311voto

nsayer Punkte 16289
for (Iterator<String> i = someIterable.iterator(); i.hasNext();) {
    String item = i.next();
    System.out.println(item);
}

Beachten Sie, dass Sie, wenn Sie die i.remove(); in Ihrer Schleife verwenden oder auf den eigentlichen Iterator zugreifen, können Sie die for ( : ) Idiom, da der eigentliche Iterator lediglich abgeleitet wird.

Wie von Denis Bueno angemerkt, funktioniert dieser Code für jedes Objekt, das die Iterable Schnittstelle .

Auch wenn die rechte Seite der for (:) Idiom ist eine array und nicht eine Iterable Objekts verwendet der interne Code einen int-Indexzähler und prüft gegen array.length stattdessen. Siehe die Spezifikation der Java-Sprache .

20 Stimmen

Ich habe festgestellt, dass der Aufruf einer while-Schleife wie while (someList.hasMoreElements()) { //do do something }} - bringt mich in die Nähe der Codierung, die ich zu finden gehofft hatte, als ich nach dieser Frage suchte.

560voto

Mikezx6r Punkte 16460

Das Konstrukt für jede gilt auch für Arrays. z.B.

String[] fruits = new String[] { "Orange", "Apple", "Pear", "Strawberry" };

for (String fruit : fruits) {
    // fruit is an element of the `fruits` array.
}

was im Wesentlichen gleichbedeutend ist mit

for (int i = 0; i < fruits.length; i++) {
    String fruit = fruits[i];
    // fruit is an element of the `fruits` array.
}

Also, allgemeine Zusammenfassung:
[nsayer] Das Folgende ist die längere Form dessen, was gerade passiert:

for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
  String item = i.next();
  System.out.println(item);
}

Beachten Sie, dass Sie, wenn Sie die i.remove(); in Ihrer Schleife verwenden oder auf den den eigentlichen Iterator zugreifen müssen, können Sie nicht das for( : ) Idiom verwenden können, da der eigentliche Iterator lediglich gefolgert wird.

(Denis Bueno)

Die Antwort von nsayer deutet es an, aber es ist erwähnenswert, dass die for(..)-Syntax des OPs Syntax funktionieren wird, wenn "someList" ein alles ist, das implementiert java.lang.Iterable implementiert -- es muss nicht eine Liste sein, oder eine Sammlung von java.util sein. Sogar Ihre eigenen Typen, können daher mit dieser Syntax verwendet werden.

2 Stimmen

Ich würde nicht sagen, dass das zweite Beispiel "im Wesentlichen äquivalent zum ersten" ist, denn wenn es sich um eine Liste primitiver Werte handelt, ändert jede Änderung, die Sie an den Werten vornehmen, nicht die ursprüngliche Liste in Beispiel 1, sondern die ursprüngliche Liste in Beispiel 2.

4 Stimmen

@Kosaro über welche Art von Änderungen sprechen Sie? In keinem der beiden Beispiele wird die Schleifenvariable geändert, d. h. fruit auf das Array auswirken.

197voto

aliteralmind Punkte 19059

Le site for-each-Schleife , hinzugefügt in Java 5 (auch "erweiterte for-Schleife" genannt), ist gleichbedeutend mit der Verwendung einer java.util.Iterator --Es ist ein syntaktischer Zucker für dieselbe Sache. Daher sollte beim Lesen der einzelnen Elemente, eines nach dem anderen und in der richtigen Reihenfolge, immer ein for-each anstelle eines Iterators gewählt werden, da dies bequemer und übersichtlicher ist.

For-each

for (int i : intList) {
   System.out.println("An element in the list: " + i);
}

Iterator

Iterator<Integer> intItr = intList.iterator();
while (intItr.hasNext()) {
   System.out.println("An element in the list: " + intItr.next());
}

Es gibt Situationen, in denen Sie eine Iterator direkt. Der Versuch, ein Element zu löschen, während ein for-each verwendet wird, kann (wird?) zum Beispiel zu einem ConcurrentModificationException .

For-each vs. for-Schleife: Grundlegende Unterschiede

Der einzige praktische Unterschied zwischen for-loop und for-each besteht darin, dass Sie im Fall von indizierbaren Objekten keinen Zugriff auf den Index haben. Ein Beispiel, wenn die grundlegende for-Schleife erforderlich ist:

for (int i = 0; i < array.length; i++) {
   if(i < 5) {
      // Do something special
   }  else {
      // Do other stuff
   }
}

Obwohl Sie manuell einen separaten Index int-variable mit for-each erstellen könnten,

int idx = -1;
for (int i : intArray) {
   idx++;
   ...
}

...es wird nicht empfohlen, da mit variablem Umfang ist nicht ideal, und die grundlegende for Schleife ist einfach das standardmäßige und erwartete Format für diesen Anwendungsfall.

For-each vs. for-Schleife: Leistung

Beim Zugriff auf Sammlungen ist ein for-each wesentlich schneller als die grundlegende for Array-Zugriff der Schleife. Beim Zugriff auf Arrays ist jedoch - zumindest bei Primitiv- und Wrapper-Arrays - der Zugriff über Indizes wesentlich schneller.

Zeitlicher Unterschied zwischen Iterator- und Indexzugriff für primitive int-Arrays

Indizes sind 23- 40 Prozent schneller als Iteratoren beim Zugriff auf int o Integer Arrays. Hier ist die Ausgabe der Testklasse am Ende dieses Beitrags, die die Zahlen in einem Array mit 100 Elementen (A ist der Iterator, B der Index) summiert:

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 358,597,622 nanoseconds
Test B: 269,167,681 nanoseconds
B faster by 89,429,941 nanoseconds (24.438799231635727% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 377,461,823 nanoseconds
Test B: 278,694,271 nanoseconds
B faster by 98,767,552 nanoseconds (25.666236154695838% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 288,953,495 nanoseconds
Test B: 207,050,523 nanoseconds
B faster by 81,902,972 nanoseconds (27.844689860906513% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,373,765 nanoseconds
Test B: 283,813,875 nanoseconds
B faster by 91,559,890 nanoseconds (23.891659337194227% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,790,818 nanoseconds
Test B: 220,770,915 nanoseconds
B faster by 155,019,903 nanoseconds (40.75164734599769% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 326,373,762 nanoseconds
Test B: 202,555,566 nanoseconds
B faster by 123,818,196 nanoseconds (37.437545972215744% faster)

Ich habe dies auch für eine Integer Array, und Indizes sind immer noch der klare Sieger, aber nur zwischen 18 und 25 Prozent schneller.

Bei Sammlungen sind Iteratoren schneller als Indizes

Für eine List de Integers sind Iteratoren jedoch der eindeutige Sieger. Ändern Sie einfach das int-Array in der Testklasse in:

List<Integer> intList = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100});

Und nehmen Sie die notwendigen Änderungen an der Testfunktion ( int[] a List<Integer> , length a size() , usw.):

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,429,929,976 nanoseconds
Test B: 5,262,782,488 nanoseconds
A faster by 1,832,852,512 nanoseconds (34.326681820485675% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,907,391,427 nanoseconds
Test B: 3,957,718,459 nanoseconds
A faster by 1,050,327,032 nanoseconds (26.038700083921256% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,566,004,688 nanoseconds
Test B: 4,221,746,521 nanoseconds
A faster by 1,655,741,833 nanoseconds (38.71935684115413% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,770,945,276 nanoseconds
Test B: 3,829,077,158 nanoseconds
A faster by 1,058,131,882 nanoseconds (27.134122749113843% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,467,474,055 nanoseconds
Test B: 5,183,149,104 nanoseconds
A faster by 1,715,675,049 nanoseconds (32.60101667104192% faster)

[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,439,983,933 nanoseconds
Test B: 3,509,530,312 nanoseconds
A faster by 69,546,379 nanoseconds (1.4816434912159906% faster)

[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,451,101,466 nanoseconds
Test B: 5,057,979,210 nanoseconds
A faster by 1,606,877,744 nanoseconds (31.269164666060377% faster)

In einem Test sind sie fast gleichwertig, aber bei Sammlungen gewinnt der Iterator.

*Dieser Beitrag basiert auf zwei Antworten, die ich auf Stack Overflow geschrieben habe:

Einige weitere Informationen: Was ist effizienter, eine for-each-Schleife oder ein Iterator?

Die vollständige Prüfklasse

Ich habe diese "Vergleiche-die-Zeit-die-es-braucht-um-zwei-Dinge-zu-tun"-Klasse nach der Lektüre diese Frage auf Stack Overflow:

import  java.text.NumberFormat;
import  java.util.Locale;

/**
   &lt;P&gt;{@code java TimeIteratorVsIndexIntArray 1000000}&lt;/P&gt;

   @see  &lt;CODE&gt;&lt;A HREF=&quot;https://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java&quot;&gt;https://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java&lt;/A&gt;&lt;/CODE&gt;
 **/
public class TimeIteratorVsIndexIntArray {

    public static final NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);

    public static final void main(String[] tryCount_inParamIdx0) {
        int testCount;

        // Get try-count from a command-line parameter
        try {
           testCount = Integer.parseInt(tryCount_inParamIdx0[0]);
        }
        catch(ArrayIndexOutOfBoundsException | NumberFormatException x) {
           throw  new IllegalArgumentException("Missing or invalid command line parameter: The number of testCount for each test. " + x);
        }

        //Test proper...START
        int[] intArray = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100};

        long lStart = System.nanoTime();
        for(int i = 0; i < testCount; i++) {
           testIterator(intArray);
        }

        long lADuration = outputGetNanoDuration("A", lStart);

        lStart = System.nanoTime();
        for(int i = 0; i < testCount; i++) {
           testFor(intArray);
        }

        long lBDuration = outputGetNanoDuration("B", lStart);

        outputGetABTestNanoDifference(lADuration, lBDuration, "A", "B");
    }

    private static final void testIterator(int[] int_array) {
       int total = 0;
       for(int i = 0; i < int_array.length; i++) {
          total += int_array[i];
       }
    }

    private static final void testFor(int[] int_array) {
       int total = 0;
       for(int i : int_array) {
          total += i;
       }
    }
    //Test proper...END

    //Timer testing utilities...START
    public static final long outputGetNanoDuration(String s_testName, long l_nanoStart) {
        long lDuration = System.nanoTime() - l_nanoStart;
        System.out.println("Test " + s_testName + ": " + nf.format(lDuration) + " nanoseconds");
        return  lDuration;
    }

    public static final long outputGetABTestNanoDifference(long l_aDuration, long l_bDuration, String s_aTestName, String s_bTestName) {
        long lDiff = -1;
        double dPct = -1.0;
        String sFaster = null;
        if(l_aDuration > l_bDuration) {
            lDiff = l_aDuration - l_bDuration;
            dPct = 100.00 - (l_bDuration * 100.0 / l_aDuration + 0.5);
            sFaster = "B";
        }
        else {
            lDiff = l_bDuration - l_aDuration;
            dPct = 100.00 - (l_aDuration * 100.0 / l_bDuration + 0.5);
            sFaster = "A";
        }
        System.out.println(sFaster + " faster by " + nf.format(lDiff) + " nanoseconds (" + dPct + "% faster)");
        return  lDiff;
   }

   //Timer testing utilities...END

}

7 Stimmen

Diese Antwort ist jetzt eine Blog-Beitrag und wurde aus zwei verwandten Antworten erstellt, die ich geschrieben habe: ici et ici . Es enthält auch eine generisch nützliche Klasse für den Vergleich der Geschwindigkeit zweier Funktionen (unten).

2 Stimmen

Nur eine kleine Anmerkung: Sie sollten nicht kategorisch sagen, dass die for(:)-Syntax immer besser für den Zugriff auf Sammlungen ist; wenn Sie eine Array-Liste verwenden, wird die for (:)-Schleife etwa 2 x langsamer sein als die Verwendung von for (int i = 0, len = arrayList.size(); i < len; i++). Ich glaube, Sie haben das in dem [Link] erwähnt ( stackoverflow.com/questions/2113216/ ), aber es ist wichtig zu betonen, dass...

0 Stimmen

@Leo Das ist ein gutes Argument. Eine ArrayList ist eine Sammlung, aber es ist durch ein Array gesichert, weshalb die normale für besser für sie ist.

138voto

MRocklin Punkte 51792

Hier ist eine Antwort, die keine Kenntnisse über Java-Iteratoren voraussetzt. Sie ist weniger präzise, aber für die Ausbildung nützlich.

Beim Programmieren schreiben wir oft Code, der wie der folgende aussieht:

char[] grades = ....
for(int i = 0; i < grades.length; i++) {   // for i goes from 0 to grades.length
    System.out.print(grades[i]);           // Print grades[i]
}

Die foreach-Syntax ermöglicht es, dieses gängige Muster in einer natürlicheren und weniger syntaktisch störenden Weise zu schreiben.

for(char grade : grades) {   // foreach grade in grades
    System.out.print(grade); // print that grade
}

Zusätzlich ist diese Syntax für Objekte wie Listen oder Sets gültig, die keine Array-Indizierung unterstützen, aber die Java Iterable-Schnittstelle implementieren.

49voto

toluju Punkte 4027

Die for-each-Schleife in Java verwendet den zugrunde liegenden Iterator-Mechanismus. Sie ist also identisch mit der folgenden:

Iterator<String> iterator = someList.iterator();

while (iterator.hasNext()) {
  String item = iterator.next();
  System.out.println(item);
}

1 Stimmen

Das ist das, was die Leute vor for-each benutzt haben, nicht die for-Anweisung im C-Stil.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X