Quelle est, le cas échéant, la différence de performances entre les deux boucles suivantes?
for (Object o: objectArrayList) {
o.DoSomething();
}
et
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
java
performance
for-loop
eflles
la source
la source
Réponses:
À partir de l'article 46 en Java efficace par Joshua Bloch:
la source
Toutes ces boucles font exactement la même chose, je veux juste les montrer avant de jeter mes deux cents.
Tout d'abord, la manière classique de parcourir List:
Deuxièmement, la méthode préférée car elle est moins sujette aux erreurs (combien de fois avez-vous fait le truc "oups, mélangé les variables i et j dans ces boucles dans des boucles"?).
Troisièmement, la boucle micro-optimisée pour:
Maintenant, les deux cents réels: au moins lorsque je les testais, le troisième était le plus rapide en comptant les millisecondes sur le temps qu'il fallait pour chaque type de boucle avec une simple opération répétée plusieurs millions de fois - cela utilisait Java 5 avec jre1.6u10 sur Windows au cas où quelqu'un serait intéressé.
Bien qu'il semble au moins que le troisième soit le plus rapide, vous devriez vraiment vous demander si vous voulez prendre le risque d'implémenter cette optimisation de judas partout dans votre code en boucle puisque d'après ce que j'ai vu, la boucle réelle n'est pas C'est généralement la partie la plus longue de tout programme réel (ou peut-être que je travaille juste sur le mauvais domaine, qui sait). Et aussi comme je l'ai mentionné dans le prétexte de la boucle Java for-each (certains y font référence en tant que boucle Iterator et d'autres en tant que boucle for-in ), vous êtes moins susceptible de rencontrer ce bogue stupide particulier lors de son utilisation. Et avant de débattre du fait que cela peut même être plus rapide que les autres, rappelez-vous que javac n'optimise pas du tout le bytecode (enfin, presque du tout de toute façon), il le compile simplement.
Si vous êtes dans la micro-optimisation et / ou si votre logiciel utilise beaucoup de boucles récursives, vous pourriez être intéressé par le troisième type de boucle. N'oubliez pas de bien comparer votre logiciel avant et après le changement des boucles for que vous avez avec cette étrange boucle micro-optimisée.
la source
get(int)
, l'autre utilise un fichierIterator
. ConsidérezLinkedList
où les performances defor(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
sont bien pires car elles le fontget(int)
n fois.La boucle for-each devrait généralement être préférée. L'approche "get" peut être plus lente si l'implémentation List que vous utilisez ne prend pas en charge l'accès aléatoire. Par exemple, si une LinkedList est utilisée, vous encourez un coût de traversée, tandis que l'approche for-each utilise un itérateur qui garde une trace de sa position dans la liste. Plus d'informations sur les nuances de la boucle for-each .
Je pense que l'article est maintenant là: nouvel emplacement
Le lien montré ici était mort.
la source
Eh bien, l'impact sur les performances est généralement insignifiant, mais n'est pas nul. Si vous regardez JavaDoc de l'
RandomAccess
interface:Et pour chaque boucle utilise la version avec itérateur, donc
ArrayList
par exemple, pour chaque boucle n'est pas la plus rapide.la source
Iterable
s, mais pas sur des tableaux. Pour les tableaux, une boucle for avec une variable d'index est utilisée. Il y a des informations à ce sujet dans le JLS .Il semble y avoir malheureusement une différence.
Si vous regardez le code d'octets généré pour les deux types de boucles, ils sont différents.
Voici un exemple du code source Log4j.
Dans /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java, nous avons une classe interne statique appelée Log4jMarker qui définit:
Avec boucle standard:
Avec for-each:
Que se passe-t-il avec cet Oracle?
J'ai essayé cela avec Java 7 et 8 sur Windows 7.
la source
Il est toujours préférable d'utiliser l'itérateur au lieu de l'indexation. Cela est dû au fait que l'itérateur est très probablement optimisé pour l'implémentation de List alors qu'indexé (appelant get) peut ne pas l'être. Par exemple LinkedList est une liste mais l'indexation à travers ses éléments sera plus lente que l'itération à l'aide de l'itérateur.
la source
foreach rend l'intention de votre code plus claire et cela est normalement préféré à une amélioration très mineure de la vitesse - le cas échéant.
Chaque fois que je vois une boucle indexée, je dois l'analyser un peu plus longtemps pour m'assurer qu'elle fait ce que je pense , par exemple, est-ce qu'elle commence à zéro, inclut-elle ou exclut le point final, etc.?
La plupart de mon temps semble être consacré à la lecture de code (que j'ai écrit ou que quelqu'un d'autre a écrit) et la clarté est presque toujours plus importante que la performance. Il est facile de rejeter les performances de nos jours car Hotspot fait un travail incroyable.
la source
Le code suivant:
Donne la sortie suivante sur mon système:
J'utilise Ubuntu 12.10 alpha avec OracleJDK 1.7 mise à jour 6.
En général, HotSpot optimise beaucoup d'indirections et d'opérations redondantes simples, donc en général, vous ne devriez pas vous en soucier à moins qu'il y en ait beaucoup dans la séquence ou qu'elles soient fortement imbriquées.
D'autre part, obtenir indexé sur LinkedList est beaucoup plus lent que d'appeler next on iterator pour LinkedList afin que vous puissiez éviter ce coup de performance tout en conservant la lisibilité lorsque vous utilisez des itérateurs (explicitement ou implicitement dans la boucle for-each).
la source
Même avec quelque chose comme ArrayList ou Vector, où "get" est une simple recherche de tableau, la deuxième boucle a toujours une surcharge supplémentaire que la première n'a pas. Je m'attendrais à ce que ce soit un peu plus lent que le premier.
la source
Le seul moyen de le savoir avec certitude est de le comparer, et même cela n'est pas aussi simple que cela puisse paraître . Le compilateur JIT peut faire des choses très inattendues à votre code.
la source
Voici une brève analyse de la différence mise en évidence par l'équipe de développement Android:
https://www.youtube.com/watch?v=MZOf3pOAM6A
Le résultat est qu'il ya est une différence, et dans des environnements très restreints avec des listes très importantes , il pourrait être une différence notable. Lors de leurs tests, le pour chaque boucle a pris deux fois plus de temps. Cependant, leurs tests étaient sur une arraylist de 400 000 entiers. La différence réelle par élément du tableau était de 6 microsecondes . Je n'ai pas testé et ils ne l'ont pas dit, mais je m'attendrais à ce que la différence soit légèrement plus grande en utilisant des objets plutôt que des primitives, mais même toujours à moins que vous ne construisiez un code de bibliothèque où vous n'avez aucune idée de l'échelle de ce qui vous sera demandé pour répéter, je pense que la différence ne vaut pas la peine d'être soulignée.
la source
Par le nom de la variable
objectArrayList
, je suppose que c'est une instance dejava.util.ArrayList
. Dans ce cas, la différence de performance serait imperceptible.En revanche, s'il s'agit d'une instance de
java.util.LinkedList
, la seconde approche sera beaucoup plus lente car laList#get(int)
s'agit d'une opération O (n).Ainsi, la première approche est toujours préférée à moins que l'index ne soit requis par la logique de la boucle.
la source
Les deux font la même chose, mais pour une utilisation de programmation facile et sûre pour chacun, il existe des possibilités d'erreur dans la deuxième façon d'utiliser.
la source
C'est bizarre que personne n'ait mentionné l'évidence - foreach alloue de la mémoire (sous la forme d'un itérateur), alors qu'une boucle for normale n'alloue aucune mémoire. Pour les jeux sur Android, c'est un problème, car cela signifie que le ramasse-miettes fonctionnera périodiquement. Dans un jeu, vous ne voulez pas que le ramasse-miettes s'exécute ... JAMAIS. N'utilisez donc pas de boucles foreach dans votre méthode de dessin (ou de rendu).
la source
La réponse acceptée répond à la question, mis à part le cas exceptionnel d'ArrayList ...
Puisque la plupart des développeurs s'appuient sur ArrayList (du moins je le crois)
Je suis donc obligé d'ajouter la bonne réponse ici.
Directement depuis la documentation développeur: -
La boucle for améliorée (parfois également appelée boucle "for-each") peut être utilisée pour les collections qui implémentent l'interface Iterable et pour les tableaux. Avec les collections, un itérateur est alloué pour effectuer des appels d'interface à hasNext () et next (). Avec une ArrayList, une boucle comptée manuscrite est environ 3 fois plus rapide (avec ou sans JIT), mais pour d'autres collections, la syntaxe améliorée de la boucle for sera exactement équivalente à l'utilisation explicite de l'itérateur.
Il existe plusieurs alternatives pour parcourir un tableau:
zero () est le plus lent, car le JIT ne peut pas encore optimiser le coût d'obtention de la longueur du tableau une fois pour chaque itération dans la boucle.
one () est plus rapide. Il extrait tout dans des variables locales, évitant les recherches. Seule la longueur de la baie offre un avantage en termes de performances.
two () est le plus rapide pour les appareils sans JIT, et impossible à distinguer de one () pour les appareils avec un JIT. Il utilise la syntaxe de boucle for améliorée introduite dans la version 1.5 du langage de programmation Java.
Par conséquent, vous devez utiliser la boucle for améliorée par défaut, mais envisagez une boucle comptée manuscrite pour l'itération ArrayList critique en termes de performances.
la source
Oui, la
for-each
variante est plus rapide que la normaleindex-based-for-loop
.for-each
utilisations de variantesiterator
. La traversée est donc plus rapide que lafor
boucle normale basée sur un index.En effet, ils
iterator
sont optimisés pour la traversée, car il pointe juste avant l'élément suivant et juste après l'élément précédent . Une des raisonsindex-based-for-loop
d'être lent est que, il doit calculer et se déplacer à chaque fois à la position de l'élément qui n'est pas avec leiterator
.la source
la source