1
votes

Boucles for et foreach - comment les créer le plus efficacement possible

Lorsque je crée une boucle for ou une boucle foreach, y a-t-il une différence d'efficacité entre les faire de ces 2 manières différentes?

Pour une boucle for:

   ArrayList<String>list = myClass.getStringList();

   for(String str: list){
   }

vs

   for(String str: myClass.getStringList()){}

pour une boucle foreach:

 for(int ind = 0; ind<myArray.size(); ind++){
 //do stuff

 }

vs

 int x = myArray.size();

 for(int ind = 0; ind<x; ind++){
 //do stuff
 }

Je sais que ce sont vraiment deux questions différentes mais je pense qu'elles sont suffisamment similaires pour justifier d'être dans la même question - correct moi si je me trompe


3 commentaires

Ce sont des micro-optimisations. La motivation de l'approche préférée doit être la lisibilité plutôt que la performance dans les exemples ci-dessus.


Faites chacun un million de fois, mesurez, obtenez la moyenne, comparez.


pour il y a une différence (mesurable?) puisque la méthode size est appelée à chaque itération; La boucle for-each est presque la même, getStringList n'est appelée qu'une seule fois ( 14.14.2. L'instruction for améliorée )


3 Réponses :


0
votes

Le cas ci-dessous est meilleur car il ne calcule la taille du tableau qu'une seule fois et il est utilisé pour vérifier à l'intérieur du look for.

for(String str: list){
   }

Ce qui suit n'est pas un cas efficace par rapport à le cas ci-dessus car il calcule la taille du tableau à chaque fois que la boucle for s'exécute.

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

Dans le cas de génériques comme

for(int ind = 0; ind<myArray.size(); ind++){
 //do stuff

 }


18 commentaires

Etes-vous sûr que c'est vrai? arraylist.size () Je pensais que c'était constant.


stackoverflow.com/questions/863469/...


Dans le cas d'une boucle for, elle est calculée en interne à chaque fois que j'ai mentionné.


En cas de génériques - quels génériques? Cet exemple n'a rien à voir avec les génériques. Mais vous devez mentionner que for-each utilise un itérateur en dessous.


@Sedrick, vous pouvez vérifier ce lien. stackoverflow.com/questions/8452317/...


@Sambit Ce lien concerne cependant Javascript.


Je suis d'accord avec @Nexevis. C'est pour Javascript. Sommes-nous sûrs que la même chose est vraie pour Java?


@Sedrick Je crois que vous avez raison de dire que ce n'est pas calculé chaque boucle, donc son libellé est désactivé, mais je pense que c'est un peu plus rapide car il ne faut pas appeler la méthode à chaque fois pour récupérer la valeur mais plutôt une valeur locale, mais qui est extrêmement mineure d'une optimisation. Cependant, si vous utilisez à nouveau la longueur dans la boucle, cela est utile.


@Nexevis, ce sera bien si vous modifiez ma réponse, je vous serai reconnaissant à tous pour vos gentils commentaires.


mais je me demande pourquoi arraylist.size () est une constante .... length d'un tableau est constante, mais la taille d'une liste peut être modifiée


Je suppose que chaque fois que la taille d'un tableau change, la variable de longueur est mise à jour.


@Carlos Heuberger Je pense que Sedrick voulait dire que c'est une variable de classe qui est définie chaque fois que l'arraylist change, pas qu'elle est déclarée comme final . Choix confus des mots. J'ai regardé l'implémentation et la taille est stockée dans ArrayList en tant que taille int privée;


mais ce n'est certainement pas une constante, au moins la méthode doit être appelée à chaque fois ... et, sur l'avant-dernier commentaire, les tableaux ont une taille constante (et length n'est pas une méthode juste un champ)


@Carlos Heuberger Oui, ce n'est pas une constante, c'est pourquoi j'ai dit un choix de mots déroutant. Il voulait simplement dire qu'il ne calcule pas à chaque fois que size () est appelé.


Ma déclaration originale disait que ArrayList.size () est constant. Ce qui est vrai.


@Sedrick Une constante serait taille int finale statique privée , non? Ce qui n'est pas le cas.


pourquoi est-ce constant? une des raisons d'utiliser list est que la taille est modifiée (il suffit d'ajouter / supprimer un élément) parlons-nous de Java ?? Première phrase (mot) de la documentation API de ArrayList : " Implémentation de tableau redimensionnable de l'interface List "


La complexité temporelle est plutôt o (1).



0
votes

Si les méthodes .size () et .getStringList () sont constantes, il n'y a pas de différence entre elles. Nous disons qu'ils sont constants si aucun calcul n'est requis à chaque appel (par exemple, si say est stocké sous forme d'entier à la place, comptez tous les éléments de la liste à chaque fois)

Mais juste comme conseil: si les performances ne sont pas une exigence actuelle , oubliez-les. Accordez toujours la priorité à la lisibilité si le code n'est pas une preuve de concept.


0 commentaires

0
votes

Vous pouvez compiler les méthodes et comparer leur bytecode pour voir les différences.

La plus grande différence se situe entre func1 et func2 func1 code > n'appelle size () qu'une seule fois, alors que func2 rappelle size () à chaque itération. Si size () n'est pas mis en cache (la plupart des implémentations le mettent en cache), alors il pourrait être coûteux de le recalculer. Même dans ce cas, la gigue peut probablement l'optimiser, en particulier avec une variable locale, car elle pourrait se rendre compte que la liste ne change pas. Mais si la liste était un membre exposé, il pourrait être nécessaire de recalculer la taille à chaque fois, car un thread séparé aurait pu modifier la liste, bien qu'il soit toujours très susceptible d'être optimisé pour supposer que la taille n'a pas changé. >

La seule différence entre func3 et fun4 est que func4 utilise un astore et un supplémentaires aload car il stocke la liste dans une variable locale, qui peut probablement être optimisée par la gigue.

En fin de compte, la meilleure façon de tester l'efficacité est de la mesurer.


private void func4();
  ... storing the list ...
   7  astore_1 [list] // these are the only extra operations 
   8  aload_1 [list]  // to store the list in a local variable
   9  invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [50]
  12  astore_3
  13  goto 33
  16  aload_3
  17  invokeinterface java.util.Iterator.next() : java.lang.Object [54] [nargs: 1]
  ... print ...
  33  aload_3
  34  invokeinterface java.util.Iterator.hasNext() : boolean [60] [nargs: 1]
  39  ifne 16
  42  return

private void func3();
  ... storing the list ...
   7  invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [50]
  10  astore_2
  11  goto 31
  14  aload_2
  15  invokeinterface java.util.Iterator.next() : java.lang.Object [54] [nargs: 1]
  ... print ...
  31  aload_2
  32  invokeinterface java.util.Iterator.hasNext() : boolean [60] [nargs: 1]
  37  ifne 14
  40  return

private void func2();
  ... storing the list ...
   8  iconst_0
   9  istore_2 [i]
  10  goto 30
  ... print ...
  27  iinc 2 1 [i] // i++
  30  iload_2 [i] // load i
  31  aload_1 [list] // load the list
  32  invokevirtual java.util.ArrayList.size() : int [18] // compute list.size()
  35  if_icmplt 13 // if i < list.size() goto 13
  38  return

private void func1();
  ... storing the list ...
   8  aload_1 [list]
   9  invokevirtual java.util.ArrayList.size() : int [18] // compute list.size()
  12  istore_2 [x] // store list.size()
  13  iconst_0 
  14  istore_3 [i]
  15  goto 35
  ... print ...
  32  iinc 3 1 [i] // i++
  35  iload_3 [i] // load i
  36  iload_2 [x] // load the already computed list.size()
  37  if_icmplt 18 // if i < list.size() goto 18
  40  return

private void func1() {
    ArrayList<String> list = new ArrayList<>();
    int x = list.size();
    for (int i = 0; i < x; i++)
        System.out.println(list.get(i));
}

private void func2() {
    ArrayList<String> list = new ArrayList<>();
    for (int i = 0; i < list.size(); i++)
        System.out.println(list.get(i));
}

private void func3() {
    for (String str : new ArrayList<String>())
        System.out.println(str);
}

private void func4() {
    ArrayList<String> list = new ArrayList<>();
    for (String str : list)
        System.out.println(str);
}


0 commentaires