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 Réponses :
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
}
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).
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.
Vous pouvez compiler les méthodes et comparer leur bytecode pour voir les différences.
La plus grande différence se situe entre La seule différence entre En fin de compte, la meilleure façon de tester l'efficacité est de la mesurer. func1 et func2 où 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é. > 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.
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);
}
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.
pouril y a une différence (mesurable?) puisque la méthodesizeest appelée à chaque itération; La bouclefor-eachest presque la même,getStringListn'est appelée qu'une seule fois ( 14.14.2. L'instruction for améliorée )