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 ? Il 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:
-
Il comando getfield viene utilizzato per ottenere variabili intere.
-
Chiama List.iterator per ottenere un'istanza dell'iteratore.
-
Chiama iterator.hasNext . Se restituisce true, dovrebbe essere chiamato il metodo iterator.next .
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: come 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.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:
GO TO FULL VERSION