Déclaration de variables à l’intérieur ou à l’extérieur d’une boucle

Pourquoi le système fonctionne-t-il correctement?

Ssortingng str; while (condition) { str = calculateStr(); ..... } 

Mais celui-ci est dit dangereux / incorrect:

 while (condition) { Ssortingng str = calculateStr(); ..... } 

Est-il nécessaire de déclarer des variables en dehors de la boucle?

La scope des variables locales doit toujours être la plus petite possible.

Dans votre exemple, je suppose que str n’est pas utilisé en dehors de la boucle while, sinon vous ne poseriez pas la question, car sa déclaration dans la boucle while ne serait pas une option, car elle ne serait pas compilée.

Donc, comme str n’est pas utilisé en dehors de la boucle, la plus petite scope possible pour str trouve dans la boucle while.

Donc, la réponse est catégorique que str doit absolument être déclaré dans la boucle while. Pas de si, pas et pas de mais.

Le seul cas où cette règle pourrait être violée est que, pour une raison quelconque, il est essentiel que chaque cycle d’horloge soit extrait du code, auquel cas vous pourriez envisager d’instancier quelque chose dans une étendue externe et de le réutiliser au lieu de le re-instancier à chaque itération d’une scope interne. Cependant, cela ne s’applique pas à votre exemple, en raison de l’immuabilité des chaînes dans java: une nouvelle instance de str sera toujours créée au début de votre boucle et elle devra être jetée à la fin de celle-ci. il n’y a aucune possibilité d’optimiser là.

EDIT: (injecter mon commentaire ci-dessous dans la réponse)

Dans tous les cas, la bonne façon de procéder est d’écrire tout votre code correctement, d’établir une exigence de performance pour votre produit, de mesurer votre produit final par rapport à cette exigence et, s’il ne le satisfait pas, d’optimiser les choses. Et ce qui finit généralement par se passer, c’est de trouver des moyens d’offrir des optimisations algorithmiques agréables en quelques endroits seulement, ce qui fait que notre programme répond à ses exigences de performance au lieu d’avoir à parcourir toute la base de code afin de presser les cycles d’horloge ici et là.

J’ai comparé le code d’octet de ces deux exemples (similaires):

Regardons 1. exemple :

 package inside; public class Test { public static void main(Ssortingng[] args) { while(true){ Ssortingng str = Ssortingng.valueOf(System.currentTimeMillis()); System.out.println(str); } } } 

Après javac Test.java , javap -c Test vous aurez:

 public class inside.Test extends java.lang.Object{ public inside.Test(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static void main(java.lang.Ssortingng[]); Code: 0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J 3: invokestatic #3; //Method java/lang/Ssortingng.valueOf:(J)Ljava/lang/Ssortingng; 6: astore_1 7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream; 10: aload_1 11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/Ssortingng;)V 14: goto 0 } 

Regardons 2. exemple :

 package outside; public class Test { public static void main(Ssortingng[] args) { Ssortingng str; while(true){ str = Ssortingng.valueOf(System.currentTimeMillis()); System.out.println(str); } } } 

Après javac Test.java , javap -c Test vous aurez:

 public class outside.Test extends java.lang.Object{ public outside.Test(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static void main(java.lang.Ssortingng[]); Code: 0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J 3: invokestatic #3; //Method java/lang/Ssortingng.valueOf:(J)Ljava/lang/Ssortingng; 6: astore_1 7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream; 10: aload_1 11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/Ssortingng;)V 14: goto 0 } 

Les observations montrent qu’il n’y a pas de différence entre ces deux exemples. C’est le résultat des spécifications de la JVM …

Mais au nom de la meilleure pratique de codage, il est recommandé de déclarer la variable dans la plus petite scope possible (dans cet exemple, elle est à l’intérieur de la boucle, car c’est le seul endroit où la variable est utilisée).

La déclaration des objects dans la plus petite scope améliore la lisibilité .

Les performances ne comptent pas pour les compilateurs actuels (dans ce scénario).
Du sharepoint vue de la maintenance, la 2ème option est meilleure.
Déclarez et initialisez les variables au même endroit, dans le cadre le plus étroit possible.

Comme l’a dit Donald Ervin Knuth :

“Nous devrions oublier les petites efficacités, disons environ 97% du temps: l’optimisation prématurée est la racine de tout mal”

C’est-à-dire une situation où un programmeur laisse des considérations de performance affecter la conception d’un morceau de code. Cela peut entraîner une conception qui n’est pas aussi propre que cela aurait pu être ou un code incorrect, car l’ optimisation complique le code et le programmeur est distrait par l’ optimisation .

si vous voulez utiliser str dehors de looop aussi; déclare le dehors. sinon, la 2ème version est correcte.

A l’intérieur, moins la variable est visible dans le meilleur.

Si vous n’avez pas besoin d’utiliser le str après la boucle while (liée à la scope), la deuxième condition, à savoir

  while(condition){ Ssortingng str = calculateStr(); ..... } 

c’est mieux depuis si vous définissez un object sur la stack uniquement si la condition est vraie. Ie l’utiliser si vous en avez besoin

Une solution à ce problème pourrait être de fournir une scope variable encapsulant la boucle while:

 { // all tmp loop variables here .... // .... Ssortingng str; while(condition){ str = calculateStr(); ..... } } 

Ils seraient automatiquement dé-référencés à la fin de la scope externe.

S’il vous plaît passer à la réponse mise à jour …

Pour ceux qui se soucient de la performance, sortez le System.out et limitez la boucle à 1 octet. En utilisant double (test 1/2) et en utilisant Ssortingng (3/4), les temps écoulés en millisecondes sont indiqués ci-dessous avec Windows 7 Professional 64 bits et JDK-1.7.0_21. Les bytecodes (également donnés ci-dessous pour test1 et test2) ne sont pas les mêmes. J’étais trop paresseux pour tester avec des objects mutables et relativement complexes.

double

Test1 a pris: 2710 ms

Test2 a pris: 2790 ms

Ssortingng (remplacez le double par une chaîne dans les tests)

Test3 a pris: 1200 msec

Test4 a pris: 3000 msec

Comstackr et obtenir le bytecode

 javac.exe LocalTest1.java javap.exe -c LocalTest1 > LocalTest1.bc public class LocalTest1 { public static void main(Ssortingng[] args) throws Exception { long start = System.currentTimeMillis(); double test; for (double i = 0; i < 1000000000; i++) { test = i; } long finish = System.currentTimeMillis(); System.out.println("Test1 Took: " + (finish - start) + " msecs"); } } public class LocalTest2 { public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); for (double i = 0; i < 1000000000; i++) { double test = i; } long finish = System.currentTimeMillis(); System.out.println("Test1 Took: " + (finish - start) + " msecs"); } } Compiled from "LocalTest1.java" public class LocalTest1 { public LocalTest1(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return public static void main(java.lang.Ssortingng[]) throws java.lang.Exception; Code: 0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 3: lstore_1 4: dconst_0 5: dstore 5 7: dload 5 9: ldc2_w #3 // double 1.0E9d 12: dcmpg 13: ifge 28 16: dload 5 18: dstore_3 19: dload 5 21: dconst_1 22: dadd 23: dstore 5 25: goto 7 28: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 31: lstore 5 33: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 36: new #6 // class java/lang/SsortingngBuilder 39: dup 40: invokespecial #7 // Method java/lang/SsortingngBuilder."":()V 43: ldc #8 // Ssortingng Test1 Took: 45: invokevirtual #9 // Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 48: lload 5 50: lload_1 51: lsub 52: invokevirtual #10 // Method java/lang/SsortingngBuilder.append:(J)Ljava/lang/SsortingngBuilder; 55: ldc #11 // Ssortingng msecs 57: invokevirtual #9 // Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 60: invokevirtual #12 // Method java/lang/SsortingngBuilder.toSsortingng:()Ljava/lang/Ssortingng; 63: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/Ssortingng;)V 66: return } Comstackd from "LocalTest2.java" public class LocalTest2 { public LocalTest2(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return public static void main(java.lang.Ssortingng[]) throws java.lang.Exception; Code: 0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 3: lstore_1 4: dconst_0 5: dstore_3 6: dload_3 7: ldc2_w #3 // double 1.0E9d 10: dcmpg 11: ifge 24 14: dload_3 15: dstore 5 17: dload_3 18: dconst_1 19: dadd 20: dstore_3 21: goto 6 24: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 27: lstore_3 28: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 31: new #6 // class java/lang/SsortingngBuilder 34: dup 35: invokespecial #7 // Method java/lang/SsortingngBuilder."":()V 38: ldc #8 // Ssortingng Test1 Took: 40: invokevirtual #9 // Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 43: lload_3 44: lload_1 45: lsub 46: invokevirtual #10 // Method java/lang/SsortingngBuilder.append:(J)Ljava/lang/SsortingngBuilder; 49: ldc #11 // Ssortingng msecs 51: invokevirtual #9 // Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 54: invokevirtual #12 // Method java/lang/SsortingngBuilder.toSsortingng:()Ljava/lang/Ssortingng; 57: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/Ssortingng;)V 60: return } 

MISE À JOUR RÉPONSE

Il n’est vraiment pas facile de comparer les performances avec toutes les optimisations JVM. Cependant, c’est un peu possible. Meilleur test et résultats détaillés dans Google Caliper

  1. Quelques détails sur le blog: Faut-il déclarer une variable dans une boucle ou avant la boucle?
  2. Dépôt GitHub: https://github.com/gunduru/jvdt
  3. Résultats de test pour la double casse et la boucle 100M (et oui tous les détails de la JVM): https://microbenchmarks.appspot.com/runs/b1cef8d1-0e2c-4120-be61-a99faff625b4

DéclaréAvant 1,759.209 DéclaréInside 2,242.308

  • Déclaré Avant 1 759,209 ns
  • Déclaré Intérieur 2,242.308 ns

Code de test partiel pour double déclaration

Ce n’est pas identique au code ci-dessus. Si vous ne faites que coder une boucle factice, la JVM la ignore, vous devez donc au moins atsortingbuer et renvoyer quelque chose. Ceci est également recommandé dans la documentation de Caliper.

 @Param int size; // Set automatically by framework, provided in the Main /** * Variable is declared inside the loop. * * @param reps * @return */ public double timeDeclaredInside(int reps) { /* Dummy variable needed to workaround smart JVM */ double dummy = 0; /* Test loop */ for (double i = 0; i < = size; i++) { /* Declaration and assignment */ double test = i; /* Dummy assignment to fake JVM */ if(i == size) { dummy = test; } } return dummy; } /** * Variable is declared before the loop. * * @param reps * @return */ public double timeDeclaredBefore(int reps) { /* Dummy variable needed to workaround smart JVM */ double dummy = 0; /* Actual test variable */ double test = 0; /* Test loop */ for (double i = 0; i <= size; i++) { /* Assignment */ test = i; /* Not actually needed here, but we need consistent performance results */ if(i == size) { dummy = test; } } return dummy; } 

Je pense que la meilleure ressource pour répondre à votre question serait la suivante:

Différence entre déclarer des variables avant ou en boucle?

Selon ma compréhension, cette chose dépendrait de la langue. IIRC Java optimise cela, donc il n’y a pas de différence, mais JavaScript (par exemple) effectuera l’allocation de la mémoire à chaque fois dans la boucle. En particulier, je pense que le deuxième fonctionnerait plus rapidement.

Déclarer Ssortingng str en dehors de la boucle wile lui permet d’être référencé à l’intérieur et à l’extérieur de la boucle while. Déclarer Ssortingng str à l’intérieur de la boucle while lui permet d’être référencé uniquement dans la boucle while.

Les variables doivent être déclarées aussi près que possible de leur utilisation.

Cela facilite l’ initialisation de RAII (acquisition de ressources) .

Il maintient l’étendue de la variable. Cela permet à l’optimiseur de mieux fonctionner.

Selon le guide de développement Android de Google, la scope de la variable devrait être limitée. S’il vous plaît vérifier ce lien:

Portée variable variable

Comme beaucoup l’ont fait remarquer,

 Ssortingng str; while(condition){ str = calculateStr(); ..... } 

n’est pas mieux que cela:

 while(condition){ Ssortingng str = calculateStr(); ..... } 

Donc, ne déclarez pas les variables en dehors de leurs étendues si vous ne les réutilisez pas …

Déclarer à l’intérieur de la boucle limite la scope de la variable respective. Tout dépend de l’exigence du projet sur la scope de la variable.

En vérité, la question posée ci-dessus est un problème de programmation. Comment aimeriez-vous programmer votre code? Où avez-vous besoin que le «STR» soit accessible? Il est inutile de déclarer une variable utilisée localement comme variable globale. Les bases de la programmation je crois.

La variable str sera disponible et réservera de l’espace en mémoire même après exécution du code.

  Ssortingng str; while(condition){ str = calculateStr(); ..... } 

La variable str ne sera pas disponible et la mémoire qui sera allouée à la variable str dans le code ci-dessous sera également libérée.

 while(condition){ Ssortingng str = calculateStr(); ..... } 

Si nous suivions le second, cela réduirait sûrement la mémoire de notre système et augmenterait les performances.

Ces deux exemples aboutissent à la même chose. Cependant, le premier vous permet d’utiliser la variable str dehors de la boucle while; le second ne l’est pas.

Je pense que la taille de l’object compte aussi. Dans un de mes projets, nous avions déclaré et initialisé un grand tableau bidimensionnel qui faisait que l’application émettait une exception de mémoire insuffisante. Nous avons déplacé la déclaration de la boucle à la place et effacé le tableau au début de chaque itération.

Attention pour presque tout le monde dans cette question: Voici un exemple de code où l’intérieur de la boucle, il peut facilement être 200 fois plus lent sur mon ordinateur avec Java 7 (et la consommation de mémoire est également légèrement différente). Mais il s’agit de l’allocation et pas seulement de la scope.

 public class Test { private final static int STUFF_SIZE = 512; private final static long LOOP = 10000000l; private static class Foo { private long[] bigStuff = new long[STUFF_SIZE]; public Foo(long value) { setValue(value); } public void setValue(long value) { // Putting value in a random place. bigStuff[(int) (value % STUFF_SIZE)] = value; } public long getValue() { // Resortingeving whatever value. return bigStuff[STUFF_SIZE / 2]; } } public static long test1() { long total = 0; for (long i = 0; i < LOOP; i++) { Foo foo = new Foo(i); total += foo.getValue(); } return total; } public static long test2() { long total = 0; Foo foo = new Foo(0); for (long i = 0; i < LOOP; i++) { foo.setValue(i); total += foo.getValue(); } return total; } public static void main(String[] args) { long start; start = System.currentTimeMillis(); test1(); System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); test2(); System.out.println(System.currentTimeMillis() - start); } } 

Conclusion: En fonction de la taille de la variable locale, la différence peut être énorme, même avec des variables moins importantes.

Juste pour dire que parfois, à l'extérieur ou à l'intérieur de la boucle, ça compte.

Vous avez un risque de NullPointerException si votre méthode calculateStr() renvoie null et que vous essayez d’appeler une méthode sur str.

Plus généralement, évitez d’avoir des variables avec une valeur nulle . Au fait, c’est plus fort pour les atsortingbuts de classe.