222 votes

Quel est le plus efficace, une boucle for-each, ou un itérateur?

Qui est le moyen le plus efficace pour parcourir une collection?

List<Integer>  a = new ArrayList<Integer>();
for (Integer integer : a) {
  integer.toString();
}

ou

List<Integer>  a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();) {
   Integer integer = (Integer) iterator.next();
   integer.toString();
}

Veuillez noter que ce n'est pas une copie exacte de ce, cet, cette, ou cette, bien que l'une des réponses à la dernière question vient de fermer. La raison que ce n'est pas dupe, c'est que la plupart de ces sont en comparant les boucles d'où vous appelez get(i) à l'intérieur de la boucle, plutôt que d'utiliser l'itérateur.

Comme l'a suggéré sur Meta je vais poster ma réponse à cette question.

284voto

Paul Wagland Points 10487

Si vous êtes juste errer sur la collecte de lire toutes les valeurs, alors il n'y a pas de différence entre l'utilisation d'un itérateur ou la nouvelle boucle for syntaxe, comme la nouvelle syntaxe utilise juste l'itérateur sous-marine.

Si toutefois, tu veux dire par la boucle de la vieille "c" de style boucle:

for(int i=0; i<list.size(); i++) {
   Object o = list.get(i);
}

Puis la nouvelle boucle for, ou itérateur, peut être beaucoup plus efficace, en fonction de la structure de données sous-jacente. La raison pour cela est que, pour certaines structures de données, get(i) est un O(n), ce qui rend la boucle un O(n2) de l'opération. Une traditionnelle liste chaînée est un exemple d'une telle structure de données. Tous les itérateurs ont comme une exigence fondamentale qu' next() devrait être un O(1), faire la boucle en O(n).

Pour vérifier que l'itérateur est utilisé sous l'eau par la nouvelle boucle for de la syntaxe, de comparer le bytecode généré à partir de deux extraits de code Java. D'abord la boucle for:

List<Integer>  a = new ArrayList<Integer>();
for (Integer integer : a)
{
  integer.toString();
}
// Byte code
 ALOAD 1
 INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
 ASTORE 3
 GOTO L2
L3
 ALOAD 3
 INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
 CHECKCAST java/lang/Integer
 ASTORE 2 
 ALOAD 2
 INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
 POP
L2
 ALOAD 3
 INVOKEINTERFACE java/util/Iterator.hasNext()Z
 IFNE L3

Et la deuxième, l'itérateur:

List<Integer>  a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();)
{
  Integer integer = (Integer) iterator.next();
  integer.toString();
}
// Bytecode:
 ALOAD 1
 INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
 ASTORE 2
 GOTO L7
L8
 ALOAD 2
 INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
 CHECKCAST java/lang/Integer
 ASTORE 3
 ALOAD 3
 INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
 POP
L7
 ALOAD 2
 INVOKEINTERFACE java/util/Iterator.hasNext()Z
 IFNE L8

Comme vous pouvez le voir, à la génération de code octet est effectivement identique, donc il n'y a pas de pénalité sur les performances à l'aide de la forme. Par conséquent, vous devez choisir la forme de la boucle qui est le plus esthétiquement attrayant pour vous, pour la plupart des gens qui seront la boucle for-each, qui a moins de code réutilisable.

111voto

Michael Krauklis Points 2145

La différence n'est pas dans la performance, mais dans la capacité. Lors de l'utilisation d'une référence directement vous avez plus de pouvoir sur l'utilisation explicite d'un type d'itérateur (p. ex. Liste.iterator() par rapport à la Liste.listIterator(), bien que dans la plupart des cas, ils renvoient la même mise en œuvre). Vous avez également la possibilité de référencer la variable d'itération dans la boucle. Cela vous permet de faire des choses comme supprimer des éléments de votre collection sans l'obtention d'un ConcurrentModificationException.

par exemple

C'est ok:

Set<Object> set = new HashSet<Object>();
// add some items to the set

Iterator<Object> setIterator = set.iterator();
while(setIterator.hasNext()){
     Object o = setIterator.next();
     if(o meets some condition){
          setIterator.remove();
     }
}

Ce n'est pas, comme on va le jeter en même temps une modification de l'exception:

Set<Object> set = new HashSet<Object>();
// add some items to the set

for(Object o : set){
     if(o meets some condition){
          set.remove(o);
     }
}

25voto

Cowan Points 17235

Pour développer Paul sa propre réponse, il a démontré que le pseudo-code est le même dans le compilateur (probablement du Soleil javac?) mais différents compilateurs ne sont pas garantis pour générer le même bytecode, droit? Pour voir quelle est la réelle différence entre les deux, nous allons aller directement à la source et vérifier le Langage Java Spécifications, spécifiquement 14.14.2, "Le renforcement de l'énoncé":

Le renforcement de la for énoncé est équivalent à une base for instruction de la forme:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    VariableModifiers(opt) Type Identifier = #i.next();    
    Statement 
}

En d'autres termes, il est requis par la JLS que les deux sont équivalents. En théorie, cela pourrait signifier la différence marginale en bytecode, mais en réalité, le renforcement de la boucle for est nécessaire pour:

  • Invoquer l' .iterator() méthode
  • Utiliser .hasNext()
  • Faire de la variable locale disponible via .next()

Donc, en d'autres termes, à toutes fins pratiques le bytecode sera identique, ou presque identiques. Il est difficile d'envisager n'importe quel compilateur de mise en œuvre de ce qui aurait pour résultat aucune différence significative entre les deux.

0voto

Frank V Points 9690

Je ne pense pas qu'il y ait une différence mais je n'ai jamais fait de comparaisons techniques. Voici ma logique. Le for (Integer integer : a) syntaxe utilise l'itérateur, donc en effet cela devrait être le même code ... Le casting, cependant, pourrait ralentir la seconde légèrement ...

-8voto

Chandan Points 9

Nous devons éviter d’utiliser des boucles traditionnelles pour travailler avec des collections. La raison simple de ce que je vais donner est que la complexité de for loop est de l'ordre de O (sqr (n)) et que la complexité de Iterator ou même la boucle for améliorée est juste O (n). Donc, cela donne une différence de performance. Il suffit de prendre une liste de 1000 éléments et de l'imprimer dans les deux sens. et également imprimer le décalage horaire pour l'exécution. Vous pouvez voir la différence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X