Frage Wie funktioniert das Java für jede Schleife?


Erwägen:

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 wie ohne die für jede Syntax?


1200
2017-09-17 16:44


Ursprung


Antworten:


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

Beachten Sie, dass wenn Sie verwenden müssen i.remove(); in Ihrer Schleife oder auf den tatsächlichen Iterator in irgendeiner Weise zugreifen, können Sie die nicht verwenden for ( : ) Idiom, da der tatsächliche Iterator nur abgeleitet wird.

Wie von Denis Bueno bemerkt wurde, funktioniert dieser Code für jedes Objekt, das den Iterable Schnittstelle.

Auch wenn die rechte Seite des for (:) Idiom ist ein array eher als ein Iterable Objekt verwendet der interne Code einen int-Indexzähler und prüft auf array.length stattdessen. Siehe die Java-Sprachspezifikation.


967
2017-09-17 16:46



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 entspricht

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

Also, zusammenfassende Übersicht:
[nsayer] Das Folgende ist die längere Form dessen, was passiert:

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

Beachten Sie, dass wenn Sie verwenden müssen   ich entferne(); in Ihrer Schleife oder Zugriff   der eigentliche Iterator in irgendeiner Weise, du   kann das For (:) Idiom nicht verwenden, da   der eigentliche Iterator ist lediglich   gefolgert.

[Denis Bueno]

Es ist durch nsays Antwort impliziert, aber   Es ist erwähnenswert, dass die OP für (..)   Die Syntax funktioniert, wenn "someList" ist   alles, was implementiert   java.lang.Iterable - hat es nicht   eine Liste oder eine Sammlung von sein   java.util. Sogar deine eigenen Typen,   daher kann mit diesem verwendet werden   Syntax.


423
2017-09-17 17:06



Hier ist eine Antwort, die keine Kenntnis von Java-Iteratoren voraussetzt. Es ist weniger präzise, ​​aber es ist nützlich für die Ausbildung.

Beim Programmieren schreiben wir oft Code, der wie folgt 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 allgemeine Muster auf eine natürlichere und weniger syntaktisch verrauschte Weise zu schreiben.

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

Außerdem ist diese Syntax für Objekte wie Listen oder Sets gültig, die Array-Indexierung nicht unterstützen, die Java Iterable-Schnittstelle jedoch implementieren.


119
2017-10-31 16:35



Das foreach Schleife, hinzugefügt in Java 5 (auch als "enhanced for loop" bezeichnet), entspricht der Verwendung von a java.util.Iterator- Es ist syntaktischer Zucker für die gleiche Sache. Daher, wenn jedes Element einzeln nacheinander gelesen wird, a foreach sollte immer über einen Iterator gewählt werden, da es bequemer und prägnanter ist.

für jede

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 verwenden müssen Iterator direkt. Versuchen Sie beispielsweise, ein Element zu löschen, während Sie a verwenden foreach kann (wird?) zu a führen ConcurrentModificationException.

foreach gegen for: Grundlegende Unterschiede

Der einzige praktische Unterschied zwischen for und foreach ist, dass Sie bei indexierbaren Objekten keinen Zugriff auf den Index haben. Ein Beispiel, wenn das Basic for Schleife ist erforderlich:

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

Obwohl Sie manuell eine separate Index-int-Variable mit erstellen könnten foreach,

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

es ist nicht zu empfehlen, da variabler Umfang ist nicht ideal, und das Grundlegende for loop ist einfach das Standardformat und das erwartete Format für diesen Anwendungsfall.

foreach gegen for: Leistung

Beim Zugriff auf Sammlungen a foreach ist wesentlich schneller als das Grundlegende for Array-Zugriff der Schleife. Beim Zugriff auf Arrays ist jedoch - zumindest bei Primitiv- und Wrapper-Arrays - der Zugriff über Indizes wesentlich schneller.

Timing des Unterschieds zwischen Iterator- und Indexzugriff für primitive Int-Arrays

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

[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 das auch für eine Integer Array und Indizes sind immer noch der klare Gewinner, aber nur zwischen 18 und 25 Prozent schneller.

Bei Auflistungen sind Iteratoren schneller als Indizes

Für ein List von IntegersIteratoren sind jedoch der klare Gewinner. Ändere einfach das int-Array in der Test-Klasse zu:

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 Test-Funktion vor (int[] zu List<Integer>, length zu size(), etc.):

[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 mit 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 Testklasse

Ich habe nach dem Lesen diese Klasse erstellt, um die Zeit zu vergleichen, die man braucht, um zwei Dinge zu tun diese Frage auf Stapelüberlauf:

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

}

114
2018-03-01 12:47



Die for-each-Schleife in Java verwendet den zugrunde liegenden Iteratormechanismus. Es ist also identisch mit dem Folgenden:

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

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

34
2017-09-17 16:46



In Java 8 Features können Sie dies verwenden:

List<String> messages = Arrays.asList("First", "Second", "Third");

void forTest(){
    messages.forEach(System.out::println);
}

Ausgabe

First
Second
Third

21
2018-04-19 15:22



Es ist impliziert durch nsayers Antwort, aber es ist erwähnenswert, dass die OP für (..) Syntax funktioniert, wenn "someList" ist etwas Das implementiert java.lang.Iterable - es muss keine Liste oder eine Sammlung von java.util sein. Selbst Ihre eigenen Typen können daher mit dieser Syntax verwendet werden.


19
2017-09-17 16:50



Eine Foreach-Loop-Syntax lautet:

for (type obj:array) {...}

Beispiel:

String[] s = {"Java", "Coffe", "Is", "Cool"};
for (String str:s /*s is the array*/) {
    System.out.println(str);
}

Ausgabe:

Java
Coffe
Is
Cool

WARNUNG: Sie können auf Array-Elemente mit der foreach-Schleife zugreifen, aber Sie können sie nicht initialisieren. Benutze das Original for Schleife dafür.

WARNUNG: Sie müssen den Typ des Arrays mit dem anderen Objekt übereinstimmen.

for (double b:s) // Invalid-double is not String

Wenn Sie Elemente bearbeiten möchten, verwenden Sie das Original for Schleife wie folgt:

for (int i = 0; i < s.length-1 /*-1 because of the 0 index */; i++) {
    if (i==1) //1 because once again I say the 0 index
        s[i]="2 is cool";
    else
        s[i] = "hello";
}

Wenn wir nun s auf die Konsole ablegen, erhalten wir:

hello
2 is cool
hello
hello

18
2017-11-07 05:35



Das Java-For-Each-Schleifenkonstrukt ermöglicht die Iteration über zwei Objekttypen:

  • T[]  (Arrays von jedem Typ)
  • java.lang.Iterable<T>

Das Iterable<T> Schnittstelle hat nur eine Methode: Iterator<T> iterator(). Dies funktioniert bei Objekten vom Typ Collection<T> weil das Collection<T> Schnittstelle erweitert Iterable<T>.


17
2017-09-17 18:04



Das Konzept einer foreach-Schleife, wie in Wikipedia erwähnt, ist unten hervorgehoben:

Im Gegensatz zu anderen For-Schleifenkonstrukten, foreach jedoch Schleifen normalerweise   pflegen kein expliziter Zähler: Sie sagen im Wesentlichen "tu das zu   alles in diesem Set ", anstatt" x-mal ". Dies vermeidet   Potenzial Aus-eins-Fehler und macht Code einfacher zu lesen.

Das Konzept einer foreach-Schleife beschreibt also, dass die Schleife keinen expliziten Zähler verwendet, was bedeutet, dass keine Indizes verwendet werden müssen, um in der Liste zu traversieren, wodurch der Benutzer vor einem einzelnen Fehler bewahrt wird. Um das allgemeine Konzept dieses Off-by-One-Fehlers zu beschreiben, nehmen wir ein Beispiel für eine Schleife, die in einer Liste mit Indizes durchlaufen werden soll.

// In this loop it is assumed that the list starts with index 0
for(int i=0; i<list.length; i++){

}

Aber angenommen, wenn die Liste mit Index 1 beginnt, dann wird diese Schleife eine Ausnahme auslösen, da sie kein Element bei Index 0 findet und dieser Fehler wird als ein Fehler nach dem anderen bezeichnet. Um diesen off-by-one Fehler zu vermeiden, wird das Konzept einer foreach-Schleife verwendet. Es kann auch andere Vorteile geben, aber das ist, was ich denke, ist das Hauptkonzept und der Vorteil der Verwendung einer foreach-Schleife.


13
2017-10-04 07:47