web-dev-qa-db-fra.com

Méthode la plus rapide pour itérer un tableau en Java: variable de boucle vs améliorée pour l'instruction

En Java, est-il plus rapide d'itérer à travers un tableau à l'ancienne,

for (int i = 0; i < a.length; i++)
    f(a[i]);

Ou en utilisant le formulaire plus concis,

for (Foo foo : a)
    f(foo);

Pour une ArrayList, la réponse est-elle la même?

Bien sûr, pour la grande majorité du code d'application, la réponse est qu'il ne fait aucune différence perceptible, donc le formulaire plus concis doit être utilisé pour la lisibilité. Cependant, le contexte que je regarde est le calcul technique intensif, avec des opérations qui doivent être effectuées des milliards de fois, donc même une petite différence de vitesse pourrait finir par être significative.

28
rwallace

Si vous parcourez un tableau en boucle, cela ne devrait pas avoir d'importance - la boucle améliorée pour utilise des accès au tableau de toute façon.

Par exemple, considérez ce code:

public static void main(String[] args)
{
    for (String x : args)
    {
        System.out.println(x);
    }
}

Décompilé avec javap -c Test on obtient (pour la méthode main):

public static void main(Java.lang.String[]);
  Code:
   0:   aload_0
   1:   astore_1
   2:   aload_1
   3:   arraylength
   4:   istore_2
   5:   iconst_0
   6:   istore_3
   7:   iload_3
   8:   iload_2
   9:   if_icmpge   31
   12:  aload_1
   13:  iload_3
   14:  aaload
   15:  astore  4
   17:  getstatic   #2; //Field Java/lang/System.out:Ljava/io/PrintStream;
   20:  aload   4
   22:  invokevirtual   #3; //Method Java/io/PrintStream.println:(Ljava/lang/String;)V
   25:  iinc    3, 1
   28:  goto    7
   31:  return

Maintenant, changez-le pour utiliser un accès explicite à un tableau:

public static void main(String[] args)
{
    for (int i = 0; i < args.length; i++)
    {
        System.out.println(args[i]);
    }
}

Cela se décompile pour:

public static void main(Java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   aload_0
   4:   arraylength
   5:   if_icmpge   23
   8:   getstatic   #2; //Field Java/lang/System.out:Ljava/io/PrintStream;
   11:  aload_0
   12:  iload_1
   13:  aaload
   14:  invokevirtual   #3; //Method Java/io/PrintStream.println:(Ljava/lang/String;)V
   17:  iinc    1, 1
   20:  goto    2
   23:  return

Il y a un peu plus de code de configuration dans la boucle for améliorée, mais ils font essentiellement la même chose. Aucun itérateur n'est impliqué. De plus, je m'attendrais à ce qu'ils obtiennent JITted dans un code encore plus similaire.

Suggestion: si vous pensez vraiment que cela pourrait faire une différence significative (ce que cela ne ferait que jamais si le corps de la boucle est absolument minuscule), alors vous devriez le comparer avec votre application réelle. C'est la seule situation qui compte.

55
Jon Skeet

Cela tombe carrément dans l'arène de micro-optimisation . Ça n'a vraiment pas d'importance. Stylistiquement, je préfère toujours le second car il est plus concis, sauf si vous avez besoin du compteur de boucles pour autre chose. Et c'est bien plus important que ce type de micro-optimisation : la lisibilité.

Cela étant dit, pour une ArrayList il n'y aura pas beaucoup de différence mais une LinkedList sera beaucoup plus efficace avec la seconde.

22
cletus

Mesure le. La réponse à toutes les questions de performances peut dépendre de la version de la machine virtuelle, du processeur, de la vitesse de la mémoire, des caches, etc. Vous devez donc la mesurer pour votre plate-forme particulière.

Personnellement, je préférerais la deuxième variante, car l'intention est plus claire. Si les performances deviennent un problème, je peux quand même les optimiser plus tard - si ce code est vraiment important pour les performances de l'ensemble de l'application.

7
Mnementh

Pour une LinkedList:

for(ClassOfElement element : listOfElements) {
  System.out.println(element.getValue());
}

Il a été répondu avant:

Y a-t-il une différence de performances entre une boucle for et une boucle for-each?

Sur un tableau ou une collection RandomAccess, vous pouvez obtenir une petite augmentation de la vitesse en faisant:

List<Object> list = new ArrayList<Object>();

for (int i=0, d=list.size(); i<d; i++) {
    something(list.get(i));
}

Mais je ne m'inquiéterais pas en général. De telles optimisations ne feront pas plus de 0,1% de différence avec votre code. Essayez d'appeler Java avec - prof pour voir où votre code passe réellement son temps.

1
gubby

Il est encore plus rapide d'utiliser le ParallelArray du framework fork-join (si vous avez un ensemble de données suffisamment grand).

0
akarnokd