web-dev-qa-db-fra.com

Pour chaque vs Iterator. Quelle sera la meilleure option

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?

42

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
}
84
Tala

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();
  ...
}
16
Ashish Chaurasia

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.

9
Aniket Thakur
3
Maleen Abewardana

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 Iterators.

2
Uwe Plonus

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);
}
0
Bob

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:

  • Crée un itérateur à l'aide de List.iterator()
  • Si Iterator.hasNext(): invoque Iterator.next() et poursuit la boucle

En 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.

 enter image description here

0
Birchlabs

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)

IciIterator a la meilleure performance et For a la moindre performance. Cependant, les performances de For-each se situent quelque part entre les deux.

0
Rahul Raina