Déclarer des variables à l'intérieur ou à l'extérieur d'une boucle [java]


Answers

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

Regardons 1. exemple :

package inside;

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

après javac Test.java , javap -c Test vous obtiendrez:

public class inside.Test extends java.lang.Object{
public inside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   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/String;)V
   14:  goto    0

}

Regardons 2. l'exemple :

package outside;

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

après javac Test.java , javap -c Test vous obtiendrez:

public class outside.Test extends java.lang.Object{
public outside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   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/String;)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 portée possible (dans cet exemple, elle est à l'intérieur de la boucle, car c'est le seul endroit où la variable est utilisée).

Question

Pourquoi ce qui suit fonctionne-t-il bien?

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

Mais celui-ci est dit dangereux / incorrect:

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

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




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




Si vous n'avez pas besoin d'utiliser la str après la boucle while (portée liée), la seconde condition

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

est meilleur puisque si vous définissez un objet sur la pile seulement si la condition est vraie. C'est à dire l'utiliser si vous en avez besoin




Je pense que la meilleure ressource pour répondre à votre question serait le post suivant:

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) fera l'allocation de mémoire entière à chaque fois dans la boucle. En Java en particulier, je pense que la seconde s'exécuterait plus vite.




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

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

Ils seraient automatiquement dé-référence lorsque la portée externe se termine.




Selon le guide Google Android Development, la portée de la variable doit être limitée. Veuillez vérifier ce lien:

Limite variable portée




La variable str sera disponible et réservera de l'espace en mémoire même après avoir été exécuté en dessous du code.

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

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

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

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




Comme beaucoup de personnes l'ont souligné,

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

n'est pas meilleur que ça:

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

Donc, ne déclarez pas les variables en dehors de leur portée si vous ne les réutilisez pas ...




Je pense que la taille de l'objet compte aussi. Dans l'un de mes projets, nous avions déclaré et initialisé un grand tableau bidimensionnel qui faisait que l'application jetait une exception de mémoire insuffisante. Nous avons plutôt déplacé la déclaration hors de la boucle 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 d'allocation et pas seulement de portée.

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()
        {
            // Retrieving 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 grandes.

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