134 votes

Mise en boîte bizarre d'entiers en Java

Je viens de voir un code similaire à celui-ci :

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a == b);

        Integer c = 100, d = 100;
        System.out.println(c == d);
    }
}

Une fois exécuté, ce bloc de code s'imprimera :

false
true

Je comprends pourquoi la première est false : parce que les deux objets sont des objets distincts, donc la == compare les références. Mais je n'arrive pas à comprendre pourquoi la deuxième instruction renvoie true ? Existe-t-il une étrange règle d'autocontrôle qui s'applique lorsque la valeur d'un nombre entier se situe dans une certaine plage ? Qu'est-ce qui se passe ici ?

120voto

Jon Skeet Points 692016

El true est en fait garantie par la spécification du langage. À partir de section 5.1.7 :

Si la valeur p mise en boîte est vraie, faux, un octet, un caractère dans l'intervalle \u0000 a \u007f ou un nombre int ou short compris entre -128 et 127, alors laissons r1 et r2 les résultats de deux quelconques de deux conversions de p. Il est toujours le cas que r1 == r2.

La discussion se poursuit, suggérant que si votre deuxième ligne de sortie est garantie, la première ne l'est pas (voir le dernier paragraphe cité ci-dessous) :

Idéalement, la mise en boîte d'une primitive donnée donnée p, donnerait toujours une référence identique. En pratique, cela n'est pas toujours possible en utilisant les techniques d'implémentation existantes. Les règles ci-dessus sont un compromis pragmatique. Le site clause finale ci-dessus exige que certaines valeurs communes soient toujours mises en boîte dans des objets indiscernables. Le site implémentation peut les mettre en cache, paresseusement ou avec empressement.

Pour d'autres valeurs, cette formulation n'autorise aucune hypothèse sur l'identité l'identité des valeurs encadrées de la part du de la part du programmeur. Cela permettrait (mais pas obligatoire) le partage de certaines ou ou de toutes ces références.

Cela garantit que dans la plupart des cas le comportement sera celui qui est souhaité, sans imposer une pénalité de performance performance, en particulier sur les petits dispositifs. Moins limités en mémoire moins limitées en mémoire pourraient, par exemple mettre en cache tous les caractères et shorts, ainsi que ainsi que les entiers et les longs dans la gamme de -32K - +32K.

41voto

Razib Points 329
public class Scratch
{
   public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;  //1
        System.out.println(a == b);

        Integer c = 100, d = 100;  //2
        System.out.println(c == d);
   }
}

Output:

false
true

La première sortie est produite pour comparer les références 'a' et 'b' - ce sont deux références différentes. Au point 1, deux références sont créées, ce qui revient à dire que -

Integer a = new Integer(1000);
Integer b = new Integer(1000);

La deuxième sortie est produite parce que le JVM essaie d'économiser de la mémoire, lorsque le Integer se situe dans une fourchette (de -128 à 127). Au point 2, aucune nouvelle référence de type Integer n'est créée pour 'd'. Au lieu de créer un nouvel objet pour la variable de référence de type Integer 'd', elle est seulement assignée à l'objet précédemment créé référencé par 'c'. Toutes ces opérations sont effectuées par JVM .

Ces règles d'économie de mémoire ne s'appliquent pas uniquement aux nombres entiers. Pour économiser de la mémoire, deux instances des objets enveloppants suivants (créés par boxing) seront toujours == lorsque leurs valeurs primitives sont les mêmes -.

  • Booléen
  • Octet
  • Caractère de \u0000 a \u007f (7f est 127 en décimal)
  • Short et Integer de -128 a 127

3 votes

Long a également un cache avec la même gamme que Integer .

11voto

Adam Crume Points 7444

Les objets entiers situés dans une certaine plage (je pense à -128 à 127) sont mis en cache et réutilisés. Les entiers en dehors de cette plage obtiennent un nouvel objet à chaque fois.

1 votes

Cette gamme peut être étendue en utilisant java.lang.Integer.IntegerCache.high propriété. Intéressant que Long n'ait pas cette option.

4voto

AmirHd Points 1004

C'est un point intéressant. Dans le livre Java efficace suggère de toujours surcharger equals pour vos propres classes. De plus, pour vérifier l'égalité de deux instances d'objets d'une classe java, utilisez toujours la méthode equals.

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a.equals(b));

        Integer c = 100, d = 100;
        System.out.println(c.equals(d));
    }
}

retours :

true
true

4voto

Omnifarious Points 25666

Je pense que Java conserve un cache de petits nombres entiers qui sont déjà "encadrés" parce qu'ils sont très courants et qu'on gagne un temps fou à réutiliser un objet existant plutôt qu'à en créer un nouveau.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X