Considérez le scénario suivant.
List<String> list = new ArrayList<>();
Maintenant, j'ai ajouté les valeurs String
pour cette liste.
J'ai utilisé les moyens suivants pour parcourir chaque élément de la liste.
Option un - utiliser for-each
for (String i : list) {
System.out.println(i);
}
Option deux - utiliser Iterator
Iterator it=list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
Je veux juste savoir s'il existe un avantage en termes de performances si j'utilise for-each
au lieu de Iterator
. Et aussi est-ce une mauvaise pratique d'utiliser Iterator maintenant un jour en Java?
for-each
est un sucre syntaxique pour utiliser iterators
(approche 2).
Vous devrez peut-être utiliser iterators
si vous devez modifier la collection dans votre boucle. La première approche lancera une exception.
for (String i : list) {
System.out.println(i);
list.remove(i); // throws exception
}
Iterator it=list.iterator();
while (it.hasNext()){
System.out.println(it.next());
it.remove(); // valid here
}
La différence réside en grande partie dans le sucre syntaxique, à la différence qu'un itérateur peut supprimer des éléments de la collection qu'il itère. Techniquement, les boucles for améliorées vous permettent de boucler tout ce qui est Itable, ce qui inclut au minimum Collections et tableaux.
Ne vous inquiétez pas des différences de performances. Une telle micro-optimisation est une distraction non pertinente. Si vous devez supprimer des éléments au fur et à mesure, utilisez un Iterator. Sinon, les boucles for ont tendance à être utilisées plus simplement parce qu'elles sont plus lisibles, à savoir:
for (String s : stringList) { ... }
contre:
for (Iterator<String> iter = stringList.iterator(); iter.hasNext(); ) {
String s = iter.next();
...
}
for-each
est une construction en boucle avancée. En interne, il crée un itérateur et itère sur la collection. Le seul avantage possible de l'utilisation d'un objet Iterator réel par rapport à la construction for-each
est que vous pouvez modifier votre collection à l'aide de méthodes d'Iterator telles que .remove()
. La modification de la collection sans utiliser les méthodes d'Iterator lors de l'itération produira une exception ConcurrentModificationException.
La meilleure façon de faire est Java 8 est,
list.forEach(System.out::println);
Voici quelques liens utiles.
Réponses simples: non et non.
En interne, la boucle for-each
crée une Iterator
pour parcourir la collection.
L'avantage d'utiliser Iterator
explicitement est que vous pouvez accéder à la méthode Iterator
s.
Si vous souhaitez remplacer des éléments de votre liste, j'irais à l'ancienne avec une boucle for
for (int nIndex=0; nIndex < list.size(); nIndex++) {
Obj obj = (Obj) list.get(nIndex);
// update list item
list.set(nIndex, obj2);
}
foreach
utilise quand même des itérateurs. C'est vraiment du sucre syntaxique.
Considérez le programme suivant:
import Java.util.List;
import Java.util.ArrayList;
public class Whatever {
private final List<Integer> list = new ArrayList<>();
public void main() {
for(Integer i : list) {
}
}
}
Compilons-le avec javac Whatever.Java
,
Et lisez le bytecode désassemblé de main()
, en utilisant javap -c Whatever
:
public void main();
Code:
0: aload_0
1: getfield #4 // Field list:Ljava/util/List;
4: invokeinterface #5, 1 // InterfaceMethod Java/util/List.iterator:()Ljava/util/Iterator;
9: astore_1
10: aload_1
11: invokeinterface #6, 1 // InterfaceMethod Java/util/Iterator.hasNext:()Z
16: ifeq 32
19: aload_1
20: invokeinterface #7, 1 // InterfaceMethod Java/util/Iterator.next:()Ljava/lang/Object;
25: checkcast #8 // class Java/lang/Integer
28: astore_2
29: goto 10
32: return
Nous pouvons voir que foreach
est compilé en un programme qui:
List.iterator()
Iterator.hasNext()
: invoque Iterator.next()
et poursuit la boucleEn ce qui concerne "pourquoi cette boucle inutile n'est-elle pas optimisée en dehors du code compilé? Nous pouvons voir qu'elle ne fait rien avec l'élément de liste": eh bien, il vous est possible de coder votre itérable de telle sorte que .iterator()
a side- effets, ou pour que .hasNext()
ait des effets secondaires ou des conséquences significatives.
Vous pouvez facilement imaginer qu'une requête itérative représentant une requête à défilement à partir d'une base de données puisse avoir un effet dramatique sur .hasNext()
(par exemple, contacter la base de données ou fermer un curseur parce que vous avez atteint la fin du jeu de résultats).
Ainsi, même si nous pouvons prouver que rien ne se passe dans le corps de la boucle… il est plus coûteux (insoluble?) De prouver que rien de significatif/de conséquence ne se produit lorsque nous itérons. Le compilateur doit laisser ce corps de boucle vide dans le programme.
Le mieux que nous puissions espérer serait un compilateur warning. Il est intéressant que javac -Xlint:all Whatever.Java
ne not nous avertisse de ce corps de boucle vide. IntelliJ IDEA le fait cependant. Certes, j'ai configuré IntelliJ pour utiliser Eclipse Compiler, mais ce n'est peut-être pas la raison.
Voici un extrait de code permettant de vérifier les performances de For-each
contre Iterator
contre for
pour la traversée de ArrayList<String>
, effectuée sur Java version 8.
long MAX = 2000000;
ArrayList<String> list = new ArrayList<>();
for (long i = 0; i < MAX; i++) {
list.add("" + i);
}
/**
* Checking with for each iteration.
*/
long A = System.currentTimeMillis();
for (String data : list) {
// System.out.println(data);
}
long B = System.currentTimeMillis();
System.out.println(B - A + "ms");
/**
* Checking with Iterator method
*/
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
// System.out.println(iterator.next());
}
long C = System.currentTimeMillis();
System.out.println(C - B + "ms");
/**
* Checking with normal iteration.
*/
for (int i = 0; i < MAX; i++) {
list.get((int) (i % (MAX - i)));
// System.out.println(list.get(i));
}
long D = System.currentTimeMillis();
System.out.println(D - C + "ms");
Valeurs de sortie moyennes:
19ms
9ms
27ms
Analyse des résultats:
Iterator
(9ms) <For-each
(19ms) <For
(27ms)Ici
Iterator
a la meilleure performance etFor
a la moindre performance. Cependant, les performances deFor-each
se situent quelque part entre les deux.