JavaRush /Blog Java /Random-ES /Pausa para el café #137. For loop o Foreach: ¿cuál es más...

Pausa para el café #137. For loop o Foreach: ¿cuál es más rápido en Java? Ocho formas eficientes de recorrer cada entrada en un mapa de Java

Publicado en el grupo Random-ES

For loop o Foreach: ¿cuál es más rápido en Java?

Fuente: Medio Cuando estaba buscando trabajo hace un par de años, una de las preguntas que me hicieron en una entrevista fue si deberíamos iterar sobre un ArrayList usando for o forEach . Pausa para el café #137.  For loop o Foreach: ¿cuál es más rápido en Java?  8 formas eficientes de recorrer cada entrada en un mapa de Java - 1El debate sobre la diferencia en las preferencias entre forEach y for existe desde hace mucho tiempo. Tenía la impresión de que forEach es más rápido. Pero al final me di cuenta de que estaba equivocado. Para su información, el bucle forEach (o bucle for mejorado ) introducido en Java 1.5 elimina el desorden y la posibilidad de error al ocultar por completo el iterador o la variable de índice. Creo que la única diferencia práctica entre for y forEach es que en el caso de objetos indexados, no tenemos acceso al índice.
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Sin embargo, podemos crear una variable de índice separada de tipo int usando forEach . Por ejemplo:
int index = -1;
for(int myint : mylist) {
 index++;
 if(index < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Escribamos una clase simple que tenga un método foreachTest() que itere a través de una lista usando forEach .
import java.util.List;

public class ForEachTest {
	List<Integer> intList;

    public void foreachTest(){
        for(Integer i : intList){

        }
    }
}
Cuando compilamos esta clase, el compilador convierte internamente el código en una implementación de iterador. Descompilé el código compilado ejecutando javap -verbose IterateListTest .
public void foreachTest();
   descriptor: ()V
   flags: ACC_PUBLIC
   Code:
     stack=1, locals=3, args_size=1
        0: aload_0
        1: getfield      #19                 // Field intList:Ljava/util/List;
        4: invokeinterface #21,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
        9: astore_2
       10: goto          23
       13: aload_2
       14: invokeinterface #27,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
       19: checkcast     #33                 // class java/lang/Integer
       22: astore_1
       23: aload_2
       24: invokeinterface #35,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
       29: ifne          13
       32: return
     LineNumberTable:
       line 9: 0
       line 12: 32
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0      33     0  this   Lcom/greekykhs/springboot/ForEachTest;
     StackMapTable: number_of_entries = 2
       frame_type = 255 /* full_frame */
         offset_delta = 13
         locals = [ class com/greekykhs/springboot/ForEachTest, top, class java/util/Iterator ]
         stack = []
       frame_type = 9 /* same */
Del código de bytes anterior vemos:
  1. El comando getfield se utiliza para obtener variables enteras.

  2. Llame a List.iterator para obtener una instancia de iterador.

  3. Llame a iterator.hasNext . Si devuelve verdadero, se debe llamar al método iterator.next .

Hagamos una prueba de rendimiento. En el método principal IterateListTest , creé una lista y la repetí usando bucles for y forEach .
import java.util.ArrayList;
import java.util.List;

public class IterateListTest {
	public static void main(String[] args) {
		List<Integer> mylist = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            mylist.add(i);
        }

        long forLoopStartTime = System.currentTimeMillis();
        for (int i = 0; i < mylist.size(); i++) {mylist.get(i);}

        long forLoopTraversalCost =System.currentTimeMillis()-forLoopStartTime;
        System.out.println("for loop traversal cost for ArrayList= "+ forLoopTraversalCost);

        long forEachStartTime = System.currentTimeMillis();
        for (Integer integer : mylist) {}

        long forEachTraversalCost =System.currentTimeMillis()-forEachStartTime;
        System.out.println("foreach traversal cost for ArrayList= "+ forEachTraversalCost);
	}
}
Y aquí está el resultado: Pausa para el café #137.  For loop o Foreach: ¿cuál es más rápido en Java?  8 formas eficientes de iterar a través de cada entrada en un mapa de Java - 2Como podemos ver, el rendimiento del bucle for es mejor que el del bucle forEach . Si usa LinkedList en lugar de ArrayList , puede ver que el rendimiento de forEach es mejor para LinkedList . ArrayList utiliza internamente matrices para almacenar elementos. Dado que las matrices son regiones contiguas de la memoria, la complejidad temporal es O (1). Esto se debe a que los datos se recuperan a través de índices. LinkedList utiliza una lista doblemente enlazada. Cuando usamos un bucle for para implementar el recorrido, comienza desde el nodo principal de la lista vinculada cada vez, por lo que la complejidad del tiempo es O (n * n).

Ocho formas eficientes de recorrer cada entrada en un mapa de Java

Fuente: Medio La semana pasada, un pasante me preguntó cómo iterar un mapa de Java. Le respondí que como es muy sencillo la respuesta a esta pregunta siempre está en Google. Después de un tiempo, me envió la dirección de la página en StackOverflow y resultó que una gran cantidad de personas estaban prestando atención a este problema. Por lo tanto, decidí detenerme en el tema de la iteración y compartir con ustedes varias formas de hacerlo. Pausa para el café #137.  For loop o Foreach: ¿cuál es más rápido en Java?  8 formas eficientes de iterar a través de cada entrada en un mapa de Java - 3

1. Usando iterador y Map.Entry

@Test
public void test1_UsingWhileAndMapEntry(){
    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
    System.out.println(i);
}

2. Usando foreach y Map.Entry

@Test
public void test2_UsingForEachAndMapEntry(){
    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
    System.out.println(i);
}

3. Usando foreach de Java 8

@Test
public void test3_UsingForEachAndJava8(){
    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    System.out.println(i[0]);
}

4. Usando keySet y foreach

@Test
public void test4_UsingKeySetAndForEach(){
    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    System.out.println(i);
}

5. Usando keySet e iterador

@Test
public void test5_UsingKeySetAndIterator(){
    long i = 0;
    Iterator<Integer> it = map.keySet().iterator();
    while (it.hasNext()) {
        Integer key = it.next();
        i += key + map.get(key);
    }
    System.out.println(i);
}

6. Uso de for y Map.Entry

@Test
public void test6_UsingForAndIterator(){
    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
    System.out.println(i);
}

7. Uso de la API de secuencia de Java 8

@Test
public void test7_UsingJava8StreamApi(){
    System. out .println(map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}

8. Uso paralelo de Java 8 Stream API

@Test
public void test8_UsingJava8StreamApiParallel(){
    System. out .println(map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}

Comparación de cada método en términos de velocidad:

public final static Integer SIZE = 1000000;
public Map<Integer, Integer> map = toMap();
public Map<Integer, Integer> toMap(){
    map = new HashMap<>(SIZE);
    for (int i = 0; i < SIZE; i++) {
        map.put(i, i);
    }
    return map;
}
Obtenemos: Pausa para el café #137.  For loop o Foreach: ¿cuál es más rápido en Java?  8 formas eficientes de recorrer cada entrada en un mapa de Java - 4Pausa para el café #137.  For loop o Foreach: ¿cuál es más rápido en Java?  8 formas eficientes de iterar a través de cada entrada en un mapa de Java - 5Pausa para el café #137.  For loop o Foreach: ¿cuál es más rápido en Java?  8 formas eficientes de iterar a través de cada entrada en un mapa de Java - 6

Conclusión

Al comparar los datos, aprendimos que el método 6 toma más tiempo y el método 8 toma más tiempo cuando el número es pequeño, pero toma menos tiempo cuando el número es significativo porque el método 8 se ejecuta simultáneamente. Lo interesante es que el orden de ejecución de la prueba siempre es for -> while -> foreach/stream y no sé por qué :(
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION