66 votes

Utilisation de l'opérateur == en Java pour comparer des objets enveloppants

Je suis en train de lire SCJP Java 6 de Kathy Sierra et Bert Bates et ce livre me perturbe énormément. À la page 245, ils déclarent que le code suivant.

Integer i1 = 1000;
Integer i2 = 1000;
if(i1 != i2)
System.out.println("different objects");

//Prints output
different objects

Ensuite, sur la page suivante, ils ont le code suivant

Integer i3 = 10;
Integer i4 = 10;
if(i3 == i4)
System.out.println("same objects");

//Prints output
same objects

Je ne sais plus où j'en suis ! Lorsque je fais mes propres essais, il semble que l'on ne puisse pas utiliser la fonction == pour comparer de la même manière que la méthode equals(). L'utilisation de la méthode == me donne toujours "false", même si les variables Integer ont la même valeur (par exemple 10). Ai-je raison ? L'utilisation de la méthode == pour comparer le même objet Integer (avec les mêmes valeurs) donne toujours un résultat "faux".

2 votes

Je pense que ce lien peut vous aider : stackoverflow.com/questions/1514910/

1 votes

1 votes

75voto

dasblinkenlight Points 264350

La clé de la réponse s'appelle objet de stage . Java internalise les petits nombres (moins de 128), donc toutes les instances de Integer(n) con n dans la gamme internée sont les mêmes. Les nombres supérieurs ou égaux à 128 ne sont pas internés, par conséquent Integer(1000) Les objets ne sont pas égaux les uns aux autres.

1 votes

Wow ! Je viens de voir ça aussi. Pourquoi ? C'est tellement déroutant. Quel est le raisonnement derrière tout ça ?

11 votes

Notez que seul l'objet obtenu à partir de littéraux, d'autoboxing et de Integer.valueOf() sont des objets internés tandis que ceux construits avec new Integer sont toujours des objets distincts

0 votes

@G.Bach Non, cela n'a rien à voir avec la taille. Les constantes de chaîne sont internées et vous pouvez appeler intern sur vos variables de chaîne pour le même effet, c'est tout. Et le pourquoi est simple : Nous devons stocker les constantes de chaîne quelque part de toute façon et pour les petits entiers, c'est juste une optimisation des performances et de la mémoire.

19voto

Steve Kuo Points 15196

Si vous regardez le code source de Integer vous verrez que Integer.valueOf(int) piscines toutes les valeurs -128 à 127. La raison en est que les petites valeurs entières sont utilisées fréquemment et méritent donc d'être mises en commun ou en cache.

Tiré directement de Integer.java :

public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}

Notez que cette mise en commun est spécifique à l'implémentation et qu'il n'y a aucune garantie de la gamme mise en commun.

Les réponses concernant les stages sont correctes dans le concept, mais incorrectes dans la terminologie. L'internage en Java implique normalement que le runtime Java effectue le pooling (comme l'internage de String). Dans le cas d'Integer, c'est la classe elle-même qui effectue la mise en commun. La magie de la JVM n'intervient pas.

1 votes

En fait, la mise en cache de Integer des objets pour int dans l'intervalle [-128, 127] est spécifié dans le document les documents de l'API Ainsi, cette partie de la gamme est en fait garantie.

7voto

haskovec Points 155

La réponse ci-dessus concernant le stage est juste. Il s'agit d'un élément à prendre en considération si vous le faites :

Integer i3 = new Integer(10);
Integer i4 = new Integer(10);

Vous n'aurez pas les nouveaux objets puisque vous avez créé explicitement de nouveaux objets. Si vous écrivez le code comme suit, il sera interné :

Integer i3 = Integer.valueOf(10);
Integer i4 = Integer.valueOf(10);

Ils seront à nouveau le même objet. Si vous jetez un coup d'œil à la méthode valueOf de la classe Integer.java dans le fichier src.zip, vous pouvez voir qu'elle vérifie si la valeur de l'int est comprise entre -128 et 127 et qu'elle appelle la nouvelle classe Integer, sinon elle la charge depuis le cache.

3voto

josefx Points 8417
Integer i1 = 1000;
Integer i2 = 1000;

Le compilateur "met en boîte" l'int 1000 comme objet Integer. Pour ce faire, il convertit la source en ce qui suit :

Integer i1 = Integer.valueOf(1000);
Integer i2 = Integer.valueOf(1000);

Maintenant valueOf pourrait être un simple appel à new Integer(1000) Cependant, la création d'un nouvel objet Integer à chaque fois qu'un objet int est mis en boîte coûterait du temps et de l'espace. Pour éviter cela, la classe Integer conserve un tableau d'objets Integer pour une gamme limitée de valeurs int.

if(value> maxRange || value< minRange){
     //not in pool return new Integer
     return new Integer(value);
}else{
     //return pooled Integer object
     //for the value, pool contains all Integer
     //values from minRange to maxRange
     return integerPool[value-minRange];
}

Le gain de vitesse par rapport à la perte de mémoire peut être ajusté en définissant la plage avec un argument jvm au début du programme (par défaut, elle est de -127 à 128).

0voto

supercat Points 25534

Lorsque l'opérateur Java == est utilisé pour comparer des éléments autres que des types primitifs, il vérifie l'égalité référentielle, même lorsque les éléments comparés sont des primitives enveloppées. En outre, l'opérateur valueOf et la déclaration d'autocomplétion générée par le compilateur sont généralement libres de renvoyer arbitrairement un nouvel objet qui ne sera pas égal à une autre référence existante, ou de renvoyer une référence à un objet existant (qui serait, bien sûr, égal à une référence préexistante identifiant le même objet). Les implémentations sont tenues de maintenir un "pool" d'objets de type Integer pour les valeurs -128 à 127, de sorte que tous les appels à Integer.valueOf sur un nombre particulier dans cette plage renverra des références au même objet, mais à part cela, une implémentation serait libre de faire quelque chose comme

static Integer [] intPool = new Integer[256];

public Integer valueOf(int n)
{
  int hash = (n*0x18675309) >>> 24;
  Integer instance = intPool[n];
  if (instance == null && instance.value != n)
  {
    instance = new Integer(n);
    intPool[hash] = instance ;
  }
  return instance;
}

Je ne m'attends pas particulièrement à ce que les implémentations Java fassent quelque chose de ce genre, car dans de nombreux cas, le ratio de "cache hit" pourrait être proche de 0 % et le temps supplémentaire passé à chercher des instances dans le cache serait perdu. Néanmoins, il n'y a jamais de garantie qu'une référence renvoyée par la fonction instanceOf ne correspondra pas à une référence précédente renvoyée par cette méthode (même si elle ne correspond pas à l'adresse de l'utilisateur). dernier renvoyée par cette méthode, certains algorithmes de mise en cache peuvent éventuellement faire en sorte qu'elle renvoie une référence de type plus tôt surtout si le pool est partagé par plusieurs threads sans verrouillage. L'absence de verrouillage ne fera jamais que le code renvoie autre chose qu'une référence à un entier avec la valeur correcte, mais pourrait provoquer des variations imprévisibles dans la comparaison des références renvoyées). Seule la référence à Integer objets créés directement à l'aide du constructeur new Integer(n) sont garantis comme étant uniques ; le code qui s'attend à ce que toute référence retournée par valueOf pour ne pas correspondre à une référence renvoyée par valueOf sans avoir réellement constaté qu'il ne correspond pas, doit être considéré comme rompu.

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