web-dev-qa-db-fra.com

Itérateur vs pour

Lors d’une interview, on m’a demandé quel était l’avantage d’utiliser un itérateur utilisant for boucle ou quel était l’avantage d’utiliser une boucle sur itérateur?

Quelqu'un peut-il répondre à cette question afin qu'à l'avenir, si je suis confronté à une question similaire, je puisse répondre à cette question?

64
rocking

Tout d’abord, il existe 2 types de boucles for, qui se comportent très différemment. On utilise des indices:

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

Ce genre de boucle n'est pas toujours possible. Par exemple, les listes ont des index, mais pas les ensembles, car ce sont des collections non ordonnées.

L'autre, la boucle foreach utilise un Iterator dans les coulisses:

for (Thing thing : list) {
    ...
}

Cela fonctionne avec tout type de collection (ou tableau) Iterable

Et enfin, vous pouvez utiliser un Iterator, qui fonctionne également avec n'importe quel Iterable:

for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    Thing t = it.next();
    ...
} 

Donc, vous avez en fait 3 boucles à comparer.

Vous pouvez les comparer en différents termes: performances, lisibilité, prédisposition aux erreurs, capacité.

Un itérateur peut faire des choses qu'une boucle foreach ne peut pas. Par exemple, vous pouvez supprimer des éléments pendant votre itération, si l'itérateur le prend en charge:

for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    Thing t = it.next();
    if (shouldBeDeleted(thing) {
        it.remove();
    }
} 

Les listes proposent également des itérateurs pouvant effectuer des itérations dans les deux sens. Une boucle foreach itère uniquement du début à la fin.

Mais un itérateur est plus dangereux et moins lisible. Quand vous n'avez besoin que d'une boucle foreach, c'est la solution la plus lisible. Avec un itérateur, vous pouvez faire ce qui suit, ce qui pourrait être un bug:

for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    System.out.println(it.next().getFoo());
    System.out.println(it.next().getBar());
} 

Une boucle foreach ne permet pas qu'un tel bug se produise.

L'utilisation d'index pour accéder aux éléments est légèrement plus efficace avec des collections sauvegardées par un tableau. Mais si vous changez d'avis et utilisez une liste LinkedList au lieu d'une liste ArrayList, la performance sera soudainement terrible, car à chaque fois que vous accéderez à list.get(i), la liste liée devra boucler tous ses éléments jusqu'à ce qu'elle . Un itérateur (et donc la boucle foreach) n'a pas ce problème. Il utilise toujours le meilleur moyen possible pour parcourir les éléments d'une collection donnée, car la collection elle-même a sa propre implémentation Iterator.

Ma règle générale est la suivante: utilisez la boucle foreach, à moins que vous n’ayez réellement besoin des capacités d’un Iterator. J'utiliserais uniquement pour une boucle avec des index avec des tableaux, lorsque j'ai besoin d'accéder à l'index à l'intérieur de la boucle.

108
JB Nizet

Avantage Itérateur:

  • Possibilité de supprimer des éléments des collections.
  • Possibilité d'avancer et de revenir en arrière en utilisant next() et previous().
  • Possibilité de vérifier s'il y a plus d'éléments ou non en utilisant hasNext().

Loop a été conçu uniquement pour itérer sur un Collection, donc si vous voulez simplement itérer sur un Collection, il est préférable d’utiliser une boucle telle que for-Each _, mais si vous voulez plus que cela, vous pouvez utiliser Iterator.

17
Salah

si vous accédez aux données par leur numéro (par exemple, "i"), la rapidité est grande lorsque vous utilisez un tableau. parce qu'il va directement à l'élément

Mais, autre structure de données (par exemple, arbre, liste), il faut plus de temps, car elle commence du premier élément à l’élément cible. quand vous utilisez la liste. Il faut du temps O (n). alors, il faut être lent.

si vous utilisez itérateur, le compilateur sait où vous vous trouvez. donc il faut O(1) (car il part de la position actuelle)

enfin, si vous utilisez uniquement un tableau ou une structure de données prenant en charge l’accès direct (par exemple, arraylist sur Java). "a [i]" est bon. mais, lorsque vous utilisez une autre structure de données, l'itérateur est plus efficace

11
user3392677

La principale différence entre Iterator et la boucle for classique, mis à part la solution évidente d'avoir ou non accès à l'index de l'élément itéré, est que l'utilisation d'Iterator extrait le code du client de l'implémentation de la collection sous-jacente, me permet de élaborer.

Lorsque votre code utilise un itérateur, sous cette forme

for(Item element : myCollection) { ... }

ce formulaire

Iterator<Item> iterator = myCollection.iterator();
while(iterator.hasNext()) {    
    Item element = iterator.next();    
    ... 
}

ou ce formulaire

for(Iterator iterator = myCollection.iterator(); iterator.hasNext(); ) {
   Item element = iterator.next();
   ...
}

Ce que dit votre code, c'est "peu m'importe le type de collection et son implémentation, je me soucie simplement de pouvoir parcourir ses éléments". Ce qui est généralement la meilleure approche, car cela rend votre code plus découplé.

D'autre part, si vous utilisez le classique pour la boucle, comme dans

for(int i = 0; i < myCollection.size(); i++) {
   Item element = myCollection.get(i);
   ...
}

Votre code dit, j'ai besoin de connaître le type de collection, parce que je dois parcourir ses éléments d'une manière spécifique, je vais peut-être aussi vérifier les valeurs NULL ou calculer un résultat en fonction de l'ordre des itérations. Ce qui rend votre code plus fragile, car si à tout moment le type de collection que vous recevez change, cela affectera le fonctionnement de votre code.

En résumé, la différence ne concerne pas tant la vitesse, ni l'utilisation de la mémoire, mais plutôt le découplage de votre code afin qu'il soit plus flexible pour faire face aux changements.

10
xburgos