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 ? Le 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 :
-
La commande getfield est utilisée pour obtenir des variables entières.
-
Appelez List.iterator pour obtenir une instance d’itérateur.
-
Appelez itérateur.hasNext . S'il renvoie vrai, la méthode iterator.next doit être appelée .
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 : Comme 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.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:
GO TO FULL VERSION