JavaRush /Blog Java /Random-FR /Pause café #137. Boucle For ou Foreach - lequel est le pl...

Pause café #137. Boucle For ou Foreach - lequel est le plus rapide en Java ? 8 façons efficaces de parcourir chaque entrée dans une carte Java

Publié dans le groupe Random-FR

Boucle For ou Foreach - lequel est le plus rapide en Java ?

Source : Medium Lorsque je cherchais un emploi il y a quelques années, l'une des questions qu'on m'a posée lors d'un entretien était de savoir si nous devions parcourir une ArrayList en utilisant for ou forEach ? Pause café #137.  Boucle For ou Foreach - lequel est le plus rapide en Java ?  8 façons efficaces de parcourir chaque entrée dans une carte Java - 1Le débat sur la différence de préférences entre forEach et for existe depuis longtemps. J'avais l'impression que forEach est plus rapide. Mais à la fin, j'ai réalisé que j'avais tort. Pour information, la boucle forEach (ou boucle for améliorée ) introduite dans Java 1.5 élimine l'encombrement et la possibilité d'erreur en masquant entièrement l'itérateur ou la variable d'index. Je pense que la seule différence pratique entre for et forEach est que dans le cas d'objets indexés, nous n'avons pas accès à l'index.
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Cependant, nous pouvons créer une variable d'index distincte de type int en utilisant forEach . Par exemple:
int index = -1;
for(int myint : mylist) {
 index++;
 if(index < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Écrivons une classe simple dotée d'une méthode foreachTest() qui parcourt une liste en utilisant forEach .
import java.util.List;

public class ForEachTest {
	List<Integer> intList;

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

        }
    }
}
Lorsque nous compilons cette classe, le compilateur convertit en interne le code en une implémentation d'itérateur. J'ai décompilé le code compilé en exécutant 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 */
À partir du bytecode ci-dessus, nous voyons :
  1. La commande getfield est utilisée pour obtenir des variables entières.

  2. Appelez List.iterator pour obtenir une instance d’itérateur.

  3. Appelez itérateur.hasNext . S'il renvoie vrai, la méthode iterator.next doit être appelée .

Lançons un test de performances. Dans la méthode principale IterateListTest , j'ai créé une liste et l'ai parcourue en utilisant les boucles for et 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);
	}
}
Et voici le résultat : Pause café #137.  Boucle For ou Foreach - lequel est le plus rapide en Java ?  8 façons efficaces de parcourir chaque entrée dans une carte Java - 2Comme on peut le voir, les performances de la boucle for sont meilleures que celles de la boucle forEach . Si vous utilisez LinkedList au lieu de ArrayList , vous pouvez voir que les performances de forEach sont meilleures pour LinkedList . ArrayList utilise en interne des tableaux pour stocker des éléments. Puisque les tableaux sont des régions contiguës de la mémoire, la complexité temporelle est O(1). En effet, les données sont récupérées via des index. LinkedList utilise une liste doublement chaînée. Lorsque nous utilisons une boucle for pour implémenter le parcours, elle commence à chaque fois à partir du nœud principal de la liste chaînée, donc la complexité temporelle est O(n*n).

8 façons efficaces de parcourir chaque entrée dans une carte Java

Source : Medium La semaine dernière, un stagiaire m'a demandé comment itérer une carte Java. J'ai répondu que comme c'est très simple, la réponse à cette question est toujours sur Google. Après un certain temps, elle m'a envoyé l'adresse de la page sur StackOverflow, et il s'est avéré qu'un grand nombre de personnes étaient attentives à ce problème. J'ai donc décidé de m'attarder sur la question de l'itération et de partager avec vous plusieurs façons de le faire. Pause café #137.  Boucle For ou Foreach - lequel est le plus rapide en Java ?  8 façons efficaces de parcourir chaque entrée dans une carte Java - 3

1. Utilisation de l'itérateur et de 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. Utilisation de foreach et 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. Utilisation de foreach à partir 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. Utilisation de keySet et foreach

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

5. Utilisation de keySet et de l'itérateur

@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. Utilisation de for et 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. Utilisation de l'API Java 8 Stream

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

8. Utilisation parallèle de l'API Java 8 Stream

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

Comparaison de chaque méthode en termes de vitesse :

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;
}
On a: Pause café #137.  Boucle For ou Foreach - lequel est le plus rapide en Java ?  8 façons efficaces de parcourir chaque entrée dans une carte Java - 4Pause café #137.  Boucle For ou Foreach - lequel est le plus rapide en Java ?  8 façons efficaces de parcourir chaque entrée dans une carte Java - 5Pause café #137.  Boucle For ou Foreach - lequel est le plus rapide en Java ?  8 façons efficaces de parcourir chaque entrée dans une carte Java - 6

Conclusion

En comparant les données, nous avons appris que la méthode 6 prend le plus de temps et la méthode 8 prend le plus de temps lorsque le nombre est petit, mais prend le moins de temps lorsque le nombre est significatif car la méthode 8 est exécutée simultanément. La chose intéressante est que l'ordre d'exécution des tests est toujours pour -> while -> foreach/stream et je ne sais pas pourquoi :(
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION