¿Cómo funciona el bucle Java 'for each'?


Answers

La construcción para cada uno también es válida para matrices. p.ej

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

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

que es esencialmente equivalente de

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

Entonces, resumen general:
[nsayer] La siguiente es la forma más larga de lo que está sucediendo:

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

Tenga en cuenta que si necesita usar i.remove (); en su bucle, o acceder al iterador real de alguna manera, no puede usar el modificador for (:), ya que el iterador real es simplemente inferido.

[Denis Bueno]

Está implícito en la respuesta de nsayer, pero vale la pena señalar que la sintaxis de OP (..) funcionará cuando "someList" sea algo que implemente java.lang.Iterable, no tiene que ser una lista o una colección de java.util. Incluso sus propios tipos, por lo tanto, se pueden utilizar con esta sintaxis.

Question

Considerar:

List<String> someList = new ArrayList<String>();
// add "monkey", "donkey", "skeleton key" to someList
for (String item : someList) {
    System.out.println(item);
}

¿Cómo sería el equivalente for loop sin usar el para cada sintaxis?




En las características de Java 8 puedes usar esto:

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

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

Salida

First
Second
Third



El docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html , agregado en Java 5 (también llamado "Enhanced for loop"), es equivalente a usar un java.util.Iterator es azúcar sintáctico para la misma cosa. Por lo tanto, al leer cada elemento, uno por uno y en orden, siempre debe elegirse un foreach sobre un iterador, ya que es más conveniente y conciso.

para cada

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

Iterador

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

Hay situaciones en las que debe usar un Iterator directamente. Por ejemplo, intentar eliminar un elemento mientras se utiliza un foreach puede (¿lo hará?) Dar como resultado una ConcurrentModificationException .

foreach vs. for : Diferencias básicas

La única diferencia práctica entre for y foreach es que, en el caso de objetos indexables, no tiene acceso al índice. Un ejemplo cuando se requiere el bucle for para:

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

Aunque puede crear manualmente una variable int de índice separada con foreach ,

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

no se recomienda, ya variable-scope no es ideal, y el bucle for el básico es simplemente el formato estándar y esperado para este caso de uso.

foreach vs. for : Rendimiento

Al acceder a las colecciones, un foreach es significativamente más rápido que el acceso básico for bucle for . Sin embargo, al acceder a las matrices, al menos con matrices primitivas y matrices contenedoras, el acceso a través de índices es mucho más rápido.

Temporización de la diferencia entre el acceso del iterador y el índice para prim-int-arrays

Los índices son un 23-40 por ciento más rápidos que los iteradores al acceder a las matrices Integer o Integer . Aquí está el resultado de la clase de prueba en la parte inferior de esta publicación, que suma los números en una matriz primitiva-int de 100 elementos (A es iterador, B es índice):

[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)

También ejecuté esto para una matriz de Integer , y los índices siguen siendo el claro ganador, pero solo entre un 18 y un 25 por ciento más rápido.

Para las colecciones, los iteradores son más rápidos que los índices

Sin embargo, para una List de Integers , los iteradores son el claro ganador. Simplemente cambie el int-array en la clase de prueba para:

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});

Y realice los cambios necesarios en la función de prueba ( int[] a List<Integer> , length to 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)

En una prueba son casi equivalentes, pero con colecciones, el iterador gana.

* Esta publicación se basa en dos respuestas que escribí en :

Algo más de información: ¿Cuál es más eficiente, un bucle for-each o un iterador?

La clase de prueba completa

Creé esta clase de compare-the-time-it-takes-to-do-any-two-things después de leer esta pregunta en :

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://.com/questions/180158/how-do-i-time-a-methods-execution-in-java&quot;&gt;https://.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

}



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



public static Boolean Add_Tag(int totalsize)
{ List<String> fullst = new ArrayList<String>();
            for(int k=0;k<totalsize;k++)
            {
              fullst.addAll();
            }
}



También tenga en cuenta que el uso del método "foreach" en la pregunta original tiene algunas limitaciones, como la imposibilidad de eliminar elementos de la lista durante la iteración.

El nuevo for-loop es más fácil de leer y elimina la necesidad de un iterador por separado, pero solo es realmente utilizable en los pases de iteración de solo lectura.




Como dicen tantas buenas respuestas, un objeto debe implementar la Iterable interface si quiere usar un bucle for-each .

Publicaré un ejemplo simple e intentaré explicar de una manera diferente cómo funciona un bucle for-each .

El ejemplo de bucle for-each :

public class ForEachTest {

    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();
        list.add("111");
        list.add("222");

        for (String str : list) {
            System.out.println(str);
        }
    }
}

Entonces, si usamos javap para descompilar esta clase, obtendremos esta muestra de código de bytes:

public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: new           #16                 // class java/util/ArrayList
         3: dup
         4: invokespecial #18                 // Method java/util/ArrayList."<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #19                 // String 111
        11: invokeinterface #21,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        16: pop
        17: aload_1
        18: ldc           #27                 // String 222
        20: invokeinterface #21,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        25: pop
        26: aload_1
        27: invokeinterface #29,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;

Como podemos ver en la última línea de la muestra, el compilador convertirá automáticamente el uso de for-each palabra clave al uso de un Iterator en el momento de la compilación. Eso puede explicar por qué objeto, que no implementa la Iterable interface , arrojará una Exception cuando intente usar el ciclo for-each .




Se vería algo como esto. Muy escabroso

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

Hay una buena descripción para cada uno en la documentación de Sun.




Esto parece loco, pero oye, funciona

List<String> someList = new ArrayList<>(); //has content
someList.forEach(System.out::println);

Esto funciona. magia




Como se define en JLS for-each loop puede tener dos formas:

  1. Si el tipo de expresión es un subtipo de Iterable , la traducción es como sigue:

    List<String> someList = new ArrayList<String>();
    someList.add("Apple");
    someList.add("Ball");
    for (String item : someList) {
        System.out.println(item);
    }
    
    // IS TRANSLATED TO:
    
    for(Iterator<String> stringIterator = someList.iterator(); stringIterator.hasNext(); ) {
        String item = stringIterator.next();
        System.out.println(item);
    }
    
  2. Si la expresión necesariamente tiene una matriz tipo T[] entonces:

    String[] someArray = new String[2];
    someArray[0] = "Apple";
    someArray[1] = "Ball";
    
    for(String item2 : someArray) {
        System.out.println(item2);
    }
    
    // IS TRANSLATED TO:
    for (int i = 0; i < someArray.length; i++) {
        String item2 = someArray[i];
        System.out.println(item2);
    }
    

Java 8 ha introducido transmisiones que funcionan generalmente mejor. Podemos usarlos como:

someList.stream().forEach(System.out::println);
Arrays.stream(someArray).forEach(System.out::println);



El constructo de bucle Java "for-each" permitirá la iteración sobre dos tipos de objetos:

  • T[] (matrices de cualquier tipo)
  • java.lang.Iterable<T>

La interfaz Iterable<T> tiene solo un método: Iterator<T> iterator() . Esto funciona en objetos de tipo Collection<T> porque la interfaz Collection<T> extiende Iterable<T> .