Performance de la boucle traditionnelle vs itérateur / foreach en Java

Est-il des résultats de tests de performances disponible en comparant traditionnel pour la boucle vs Itérateur tout en parcourant une liste de tableaux,HashMap et d'autres collections?

ou tout simplement pourquoi devrais-je utiliser Iterator over pour boucle ou vice versa?

55
demandé sur aliteralmind 2009-12-10 10:39:15

9 réponses

en supposant que c'est ce que vous vouliez dire:

// traditional for loop
for (int i = 0; i < collection.size(); i++) {
  T obj = collection.get(i);
  // snip
}

// using iterator
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
  T obj = iter.next();
  // snip
}

// using iterator internally (confirm it yourself using javap -c)
for (T obj : collection) {
   // snip
}

Iterator est plus rapide pour les collections sans accès aléatoire (par exemple TreeSet, HashMap, LinkedList). Pour les tableaux et les tableaux, les différences de performance devraient être négligeables.

Edit: je crois que le micro-benchmarking est à la racine du mal, tout comme l'optimisation précoce. Mais là encore, je pense que c'est bon de ressentir les conséquences de ces tout à fait insignifiantes. Donc j'ai exécuter un petit essai :

  • itérer sur une LinkedList et une liste de tableaux respecively
  • 100 000 "aléatoire" chaînes
  • résumé de leur longueur (juste quelque chose pour éviter que le compilateur optimise loin toute la boucle)
  • utilisant les 3 styles de boucle (itérateur, pour chacun, pour avec compteur)

les Résultats sont similaires pour tous, mais avec de counter " avec LinkedList. Les cinq autres ont mis moins de 20 millisecondes à se répéter sur toute la liste. L'utilisation de list.get(i) sur une liste de liens 100.000 fois a pris plus de 2 minutes (!) à compléter (60 000 fois plus lent). Wow! :) Par conséquent, il est préférable d'utiliser un itérateur (explicitement ou implicitement en utilisant pour chacun), surtout si vous ne savez pas quel type et quelle taille de liste vous traitez.

75
répondu sfussenegger 2011-06-06 07:19:17

la première raison d'utiliser un itérateur est évidence d'exactitude . Si vous utilisez un index manuel, il peut y avoir des erreurs très inoffensives que vous ne pouvez voir que si vous regardez de très près: avez-vous commencé à 1 ou à 0? Avez-vous fini à length - 1 ? Avez-vous utilisé < ou <= ? Si vous utilisez un itérateur, il est beaucoup plus facile de voir qu'il itère vraiment l'ensemble du tableau. "Dis ce que tu fais, fais ce que tu dis."

le la deuxième raison est l'accès uniforme à différentes structures de données. Un tableau peut être accédé efficacement par un index, mais une liste liée est le mieux traversé en se rappelant le dernier élément accédé (sinon, vous obtenez un " Shlemiel le peintre "). Un hashmap est encore plus compliqué. En fournissant une interface uniforme à partir de ces structures de données et d'autres (par exemple, vous pouvez également faire des travers-arbres), vous obtenez à nouveau une exactitude évidente. La logique de traversée doit être mise en œuvre une seule fois, et le code qui l'utilise peut dire "ce qu'il fait, et faire ce qu'il dit."

22
répondu Svante 2009-12-10 08:25:52
La Performance

est similaire dans la plupart des cas.

Cependant, chaque fois qu'un code reçoit une liste, et des boucles sur elle, il y a un cas bien connu:

L'itérateur est bien meilleur pour toutes les implémentations de liste qui n'implémentent pas RandomAccess (exemple: LinkedList).

la raison en est que pour ces listes, accéder à un élément par index n'est pas une opération à temps constant.

donc vous pouvez aussi considérez L'itérateur comme plus robuste (aux détails de la mise en œuvre).


comme toujours, les performances ne doivent pas être masquées des problèmes de lisibilité.

La boucle foreach java5 est un grand succès sur cet aspect: -)

4
répondu KLE 2009-12-10 07:50:45

Je ne crois pas que

for (T obj : collection) {

calcule .taille () à chaque fois à travers la boucle et est donc plus rapide que

for (int i = 0; i < collection.size(); i++) {
2
répondu MeBigFatGuy 2009-12-12 06:13:23

L'une des meilleures raisons d'utiliser un itérateur au-dessus de la syntaxe i++ est que toutes les structures de données ne supporteront pas l'accès aléatoire, encore moins qu'il fonctionne bien. Vous devriez également programmer vers la liste ou l'interface de collecte de sorte que si vous plus tard décidé qu'une autre structure de données serait plus efficace, vous seriez en mesure de l'échanger sans chirurgie massive. Dans ce cas (le cas du codage à une interface) vous ne connaîtrez pas nécessairement les détails de la mise en œuvre et il est probablement il est plus sage de s'en remettre à la structure des données elle-même.

1
répondu Jason Tholstrup 2009-12-10 08:02:43

L'une des raisons pour lesquelles j'ai appris à coller avec le pour chacun est qu'il simplifie boucles imbriquées, en particulier sur 2+ boucles dimensionnelles. Tous les "i", " j " et " k " que vous pourriez manipuler peuvent devenir confus très rapidement.

1
répondu Ashton K 2009-12-10 08:07:25

utilisez JAD ou JD-GUI contre votre code généré, et vous verrez qu'il n'y a pas de différence réelle. L'avantage de la nouvelle itérateur est qu'il ressemble de nettoyage dans votre base de code.

Edit : je vois dans les autres réponses que vous vouliez dire la différence entre utiliser get(i) et un itérateur. J'ai pris la question originale pour faire la différence entre les anciennes et les nouvelles façons de en utilisant l'itérateur.

utiliser get(i) et maintenir votre propre compteur, en particulier pour les classes List n'est pas une bonne idée, pour les raisons mentionnées dans la réponse acceptée.

1
répondu Paul Wagland 2009-12-10 09:13:34

Oui, Cela fait une différence sur les collections qui ne sont pas basées sur un accès aléatoire comme LinkedList. Une liste liée en interne est implémentée par des noeuds pointant vers le prochain(à partir d'un noeud principal).

la méthode get(i) dans une liste liée commence à partir du noeud principal et navigue à travers les liens jusqu'au nœud de l'IE. Lorsque vous itérez sur la liste liée à l'aide d'une boucle traditionnelle pour, vous commencez à nouveau à partir du noeud de tête à chaque fois, donc l'ensemble traversal devient temps quadratique.

for( int i = 0; i< list.size(); i++ ) {
    list.get(i); //this starts everytime from the head node instead of previous node
}

alors que le pour chaque boucle itère sur l'itérateur obtenu à partir de la liste liée et appelle sa méthode next (). L'itérateur maintient les États du dernier accès et ne démarre donc pas à chaque fois depuis head.

for( Object item: list ) {
    //item element is obtained from the iterator's next method.
}
1
répondu mickeymoon 2015-02-05 12:19:53

+1 à ce que sfussenegger a dit. Pour votre information, que vous utilisiez un itérateur explicite ou un itérateur implicite (c'est-à-dire pour chaque itérateur) ne fera pas de différence de performance car ils compilent avec le même code octet.

0
répondu erturne 2009-12-10 10:38:50