JavaRush /Java Blog /Random-IT /Pausa caffè #137. For loop o Foreach: quale è più veloce ...

Pausa caffè #137. For loop o Foreach: quale è più veloce in Java? 8 modi efficienti per scorrere ciascuna voce in una mappa Java

Pubblicato nel gruppo Random-IT

For loop o Foreach: quale è più veloce in Java?

Fonte: Medium Quando stavo cercando un lavoro un paio di anni fa, una delle domande che mi è stata posta in un'intervista era se dovremmo scorrere un ArrayList utilizzando for o forEach ? Pausa caffè #137.  For loop o Foreach: quale è più veloce in Java?  8 modi efficienti per scorrere ciascuna voce in una mappa Java - 1Il dibattito sulla differenza nelle preferenze tra forEach e for è in corso da molto tempo. Avevo l'impressione che forEach fosse più veloce. Ma alla fine ho capito che mi sbagliavo. Per tua informazione, il ciclo forEach (o ciclo for migliorato ) introdotto in Java 1.5 elimina la confusione e la possibilità di errore nascondendo completamente l'iteratore o la variabile indice. Credo che l'unica differenza pratica tra for e forEach sia che nel caso di oggetti indicizzati non abbiamo accesso a index.
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Tuttavia, possiamo creare una variabile indice separata di tipo int utilizzando forEach . Per esempio:
int index = -1;
for(int myint : mylist) {
 index++;
 if(index < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Scriviamo una classe semplice che abbia un metodo foreachTest() che scorre un elenco utilizzando forEach .
import java.util.List;

public class ForEachTest {
	List<Integer> intList;

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

        }
    }
}
Quando compiliamo questa classe, il compilatore converte internamente il codice in un'implementazione dell'iteratore. Ho decompilato il codice compilato eseguendo 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 */
Dal bytecode sopra vediamo:
  1. Il comando getfield viene utilizzato per ottenere variabili intere.

  2. Chiama List.iterator per ottenere un'istanza dell'iteratore.

  3. Chiama iterator.hasNext . Se restituisce true, dovrebbe essere chiamato il metodo iterator.next .

Eseguiamo un test delle prestazioni. Nel metodo principale IterateListTest , ho creato un elenco e l'ho ripetuto utilizzando i cicli for e 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);
	}
}
Ed ecco il risultato: Pausa caffè #137.  For loop o Foreach: quale è più veloce in Java?  8 modi efficienti per scorrere ogni voce in una mappa Java - 2come possiamo vedere, le prestazioni del ciclo for sono migliori rispetto al ciclo forEach . Se usi LinkedList invece di ArrayList , puoi vedere che le prestazioni di forEach sono migliori per LinkedList . ArrayList utilizza internamente gli array per memorizzare gli elementi. Poiché gli array sono regioni contigue di memoria, la complessità temporale è O(1). Questo perché i dati vengono recuperati tramite indici. LinkedList utilizza un elenco doppiamente collegato. Quando utilizziamo un ciclo for per implementare l'attraversamento, inizia ogni volta dal nodo principale della lista concatenata, quindi la complessità temporale è O(n*n).

8 modi efficienti per scorrere ciascuna voce in una mappa Java

Fonte: Medium La scorsa settimana, uno stagista mi ha chiesto come iterare una mappa Java. Ho risposto che essendo molto semplice la risposta a questa domanda si trova sempre su Google. Dopo un po', mi ha inviato l'indirizzo della pagina su StackOverflow e si è scoperto che un numero enorme di persone prestava attenzione a questo problema. Pertanto, ho deciso di soffermarmi sulla questione dell'iterazione e di condividere con voi diversi modi per farlo. Pausa caffè #137.  For loop o Foreach: quale è più veloce in Java?  8 modi efficienti per scorrere ciascuna voce in una mappa Java - 3

1. Utilizzo dell'iteratore e 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. Utilizzo di foreach e 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. Utilizzo di foreach da 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. Utilizzo di keySet e foreach

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

5. Utilizzo di keySet e iteratore

@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. Utilizzo di for e 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. Utilizzo dell'API Java 8 Stream

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

8. Utilizzo parallelo dell'API Java 8 Stream

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

Confronto di ciascun metodo in termini di velocità:

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;
}
Noi abbiamo: Pausa caffè #137.  For loop o Foreach: quale è più veloce in Java?  8 modi efficienti per scorrere ciascuna voce in una mappa Java - 4Pausa caffè #137.  For loop o Foreach: quale è più veloce in Java?  8 modi efficienti per scorrere ogni voce in una mappa Java - 5Pausa caffè #137.  For loop o Foreach: quale è più veloce in Java?  8 modi efficienti per scorrere ogni voce in una mappa Java - 6

Conclusione

Dal confronto dei dati, abbiamo appreso che il metodo 6 impiega più tempo e il metodo 8 impiega più tempo quando il numero è piccolo, ma impiega meno tempo quando il numero è significativo perché il metodo 8 viene eseguito simultaneamente. La cosa interessante è che l'ordine di esecuzione del test è sempre for -> while -> foreach/stream e non so perché :(
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION