web-dev-qa-db-fra.com

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

Je lis SCJP Java 6 de Kathy Sierra et Bert Bates et ce livre m'embrouille énormément. Sur la page 245 ils indiquent que le code suivant ci-dessous.

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

//Prints output
different objects

Puis à 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 suis tellement confus! Lorsque j'essaie moi-même, il semble que vous ne pouvez pas utiliser == pour comparer de la même manière que vous utiliseriez la méthode equals (). L'utilisation de == me donne toujours 'false' même si les variables Integer ont la même valeur (c'est-à-dire 10). Ai-je raison? Utiliser le == pour comparer le même objet Integer (avec les mêmes valeurs) aura toujours pour résultat 'false'

46
dido

La clé de la réponse s'appelle object interning . Java interne de petits nombres (moins de 128), toutes les instances de Integer(n) avec n dans la plage internée sont identiques Les nombres supérieurs ou égaux à 128 n'étant pas internés, les objets Integer(1000) ne sont donc pas égaux.

58
dasblinkenlight

Si vous examinez le code source de Integer, vous verrez que Integer.valueOf(int) pools toutes les valeurs -128 à 127. La raison en est que les petites valeurs Integer sont utilisées fréquemment et méritent donc d'être mises en pool/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 ce pooling est spécifique à l'implémentation et qu'il n'y a aucune garantie de la plage poolée.

Les réponses à propos de l'internalisation sont correctes dans leur concept, mais incorrectes dans leur terminologie. Interning in Java implique normalement que le runtime Java effectue le regroupement (tel que le stagiaire de String). Dans le cas d'Integer, c'est la classe elle-même qui fait la mise en commun. Il n'y a pas de magie JVM impliquée.

17
Steve Kuo

La réponse ci-dessus à propos de l'internat est juste. Quelque chose à considérer si vous 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 maintenant le même objet à nouveau. Si vous examinez la méthode valueOf à l'intérieur de la classe Integer.Java du fichier src.Zip, vous pouvez voir où il vérifie si la valeur de l'int est en dehors de -128 à 127, sinon la nouvelle classe Integer est appelée. il le charge depuis le cache.

7
haskovec
Integer i1 = 1000;
Integer i2 = 1000;

Le compilateur 'encadre' l'int 1000 en tant qu'objet Integer. Pour ce faire, il convertit le source comme suit:

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

Désormais, valueOf pourrait être un simple appel à new Integer(1000); toutefois, créer un nouvel objet Integer à chaque fois qu'une case int serait encadrée coûterait à la fois du temps et de l'espace. Pour éviter cela, la classe Integer conserve un tableau d'objets Integer pour une plage 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];
}

La vitesse gagnée par rapport à la mémoire perdue peut être ajustée en définissant la plage avec un argument jvm au début du programme (la valeur par défaut est comprise entre -127 et 128).

2
josefx

Selon jls-5.1.7

If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f,   
or an int or short number between -128 and 127 (inclusive), then let r1 and r2 
be the results of any two boxing conversions of p. It is always the case that r1 == r2.

Ainsi, tout nombre compris entre -128 et 127 est mis en cache par la classe Interger.
Rappelez-vous, lorsque vous comparez deux objets, utilisez toujours la méthode equals

Le code de mise en cache est écrit dans la classe IntegerCache qui est membre de la classe Integer.

Voici l'extrait de code:

 /**
 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
 *
 * The cache is initialized on first usage.  The size of the cache
 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 * During VM initialization, Java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * Sun.misc.VM class.
 */

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            Sun.misc.VM.getSavedProperty("Java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

Références

0
Aniket Kulkarni

La comparaison des chaînes et la comparaison des nombres entiers avec == et! = Donnent des résultats booléens pas comme prévu. Veillez donc à ce que les résultats inconnus ne compromettent pas les performances, la fiabilité et la précision de votre logiciel.

Lorsque l'opérateur Java == est utilisé pour comparer autre chose que des types primitifs, il vérifie l'égalité référentielle; Cela s'applique même lorsque les objets comparés sont des primitives encapsulées. De plus, la méthode valueOf et l’instruction de liste déroulante automatique générée par le compilateur sont généralement libres de renvoyer arbitrairement un nouvel objet qui ne sera pas une référence égale à une autre référence existante, ou de renvoyer une référence à un objet existant (ce qui, bien sûr, , soit référence égale à toute référence préexistante identifiant le même objet). Les implémentations sont tenues de gérer un "pool" d'instances Integer pour les valeurs -128 à 127, de sorte que tous les appels à Integer.valueOf sur un nombre particulier dans cette plage renverront des références au même objet, mais 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 "nombre de requêtes en cache" pourrait être proche de 0% et le temps supplémentaire consacré à la recherche d'instances dans le cache serait une perte de temps. Néanmoins, rien ne garantit qu’une référence renvoyée par instanceOf ne corresponde pas à une référence précédente renvoyée par cette méthode (même si elle ne correspond pas à la référence last renvoyée par cette méthode, certains algorithmes de mise en cache peuvent éventuellement provoquer il renvoie une référence antérieure , en particulier si le pool est partagé par plusieurs threads sans verrouillage. Le manque de verrouillage ne fera jamais que le code retourne autre chose qu'une référence à un entier avec la valeur correcte, mais peut provoquer variations imprévisibles dans lesquelles les références renvoyées se comparent égales). Seules les références aux objets Integer créés directement à l'aide du constructeur new Integer(n) sont garanties comme étant uniques; le code qui s'attend à ce que toute référence renvoyée par valueOf ne corresponde à aucune référence renvoyée par valueOf, sans avoir réellement observé qu'elle ne correspond pas, doit être considéré comme cassé.

0
supercat

"==" compare toujours les emplacements de mémoire ou les références d'objet des valeurs. La méthode equals compare toujours les valeurs. mais equals utilise également indirectement l'opérateur "==" pour comparer les valeurs. Integer utilise le cache Integer pour stocker les valeurs comprises entre -128 et + 127.Si l'opérateur == est utilisé pour rechercher des valeurs comprises entre -128 et 127, il renvoie la valeur true. si une valeur comprise entre -128 et 127 comme 

Integer i1 = -128; 
Integer i2 = -128; 
System.out.println(i1 == i2); // returns true

autre que la plage ci-dessus, il retourne faux

Integer i1 = 1000;
Integer i2 = 1000;
System.out.println(i1 == i2); // returns false

Référez-vous au lien pour quelques informations supplémentaires 

0
vijay