Pourquoi 128 == 128 est-il faux mais 127 == 127 est vrai lors de la comparaison des wrappers Integer en Java?

173
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

Production:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

Production:

true

Remarque: les nombres entre -128 et 127 sont vrais.

vipin k.
la source
10
Vous trouverez peut-être bexhuff.com/2006/11/java-1-5-autoboxing-wackyness informatif.
Dominic Rodger
1
comment en êtes-vous arrivé à poser cette question? c'est vraiment amusant, mais on ne rencontre jamais quelque chose comme ça "dans le monde réel" ... ou?
Mare Infinitus

Réponses:

218

Lorsque vous compilez un littéral numérique en Java et que vous l'assignez à un entier (majuscule I), le compilateur émet:

Integer b2 =Integer.valueOf(127)

Cette ligne de code est également générée lorsque vous utilisez la boîte automatique.

valueOf est implémenté de telle sorte que certains nombres soient "regroupés", et il renvoie la même instance pour les valeurs inférieures à 128.

À partir du code source java 1.6, ligne 621:

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

La valeur de highpeut être configurée sur une autre valeur, avec la propriété système.

-Djava.lang.Integer.IntegerCache.high = 999

Si vous exécutez votre programme avec cette propriété système, il affichera true!

La conclusion évidente: ne vous fiez jamais à deux références identiques, comparez-les toujours à la .equals()méthode.

Ainsi b2.equals(b3)affichera vrai pour toutes les valeurs logiquement égales de b2, b3.

Notez que le Integercache n'est pas là pour des raisons de performances, mais plutôt pour se conformer au JLS, section 5.1.7 ; l'identité d'objet doit être donnée pour les valeurs -128 à 127 inclus.

Integer # valueOf (int) documente également ce comportement:

cette méthode est susceptible de générer des performances d'espace et de temps nettement meilleures en mettant en cache les valeurs fréquemment demandées. Cette méthode mettra toujours en cache les valeurs comprises entre -128 et 127, inclusivement, et peut mettre en cache d'autres valeurs en dehors de cette plage.

Andreas Petersson
la source
1
notez que les valeurs inférieures à 127 seront ignorées par java et les valeurs supérieures à Integer.MAX_VALUE-128 seront plafonnées.
Andreas Petersson
Les entiers sont mis en cache pour les valeurs d'octets dans Java 5 et les versions ultérieures, ce qui donne un nouvel Integer (1) == new Integer (1). Cependant, ce n'est pas le cas dans Java 1.4 ou version antérieure, alors méfiez-vous si vous devez éventuellement revenir à cet environnement.
MetroidFan2002
11
non, c'est faux. new Integer (1) == new Integer (1) est faux quel que soit le jvm. AFAIK aucun compilateur ne trichera avec le mot-clé «nouveau». il DOIT toujours instancier un nouvel objet.
Andreas Petersson
1
@Holger point intéressant. Mais il est techniquement possible de remplacer la classe Integer du JDK par un impl personnalisé ... (ne demandez pas pourquoi quelqu'un serait aussi fou) - alors cela pourrait avoir des effets secondaires qui ne sont pas autorisés à s'optimiser
Andreas Petersson
1
@AndreasPetersson bien sûr. «Compilateur» signifie le compilateur JIT, qui connaît précisément la classe d'implémentation réelle et ne peut optimiser que si le constructeur n'a pas d'effets secondaires. Ou optimisez l'expression pour ne reproduire que les effets secondaires, puis utilisez false. En fait, cela peut déjà se produire aujourd'hui, en tant qu'effet secondaire de l'application de l'analyse d'évasion et du remplacement scalaire.
Holger
24

Autoboxing met en cache -128 à 127. Ceci est spécifié dans le JLS ( 5.1.7 ).

Si la valeur p étant encadrée est true, false, un octet, un caractère dans la plage \ u0000 à \ u007f, ou un entier ou un nombre court entre -128 et 127, alors laissez r1 et r2 les résultats de deux conversions de boxe de p. C'est toujours le cas que r1 == r2.

Une règle simple à retenir lorsque vous traitez avec des objets est - utilisez .equalssi vous voulez vérifier si les deux objets sont "égaux", utilisez ==quand vous voulez voir s'ils pointent vers la même instance.

Michael Lloyd Lee mlk
la source
1
Remarque: le JLS a changé dans Java 9. Ceci n'est désormais garanti que pour les expressions de constante de compilation ; voir la mise à jour de la réponse acceptée.
Stephen C
9

L'utilisation de types de données primitifs, ints, produirait vrai dans les deux cas, la sortie attendue.

Cependant, puisque vous utilisez des objets Integer, l'opérateur == a une signification différente.

Dans le contexte des objets, == vérifie si les variables font référence à la même référence d'objet.

Pour comparer la valeur des objets, vous devez utiliser la méthode equals () Eg

 b2.equals(b1)

qui indiquera si b2 est inférieur à b1, supérieur ou égal à (consultez l'API pour plus de détails)

Chrisbunney
la source
7

C'est l'optimisation de la mémoire en Java.

Pour économiser de la mémoire, Java «réutilise» tous les objets wrapper dont les valeurs se situent dans les plages suivantes:

Toutes les valeurs booléennes (vrai et faux)

Toutes les valeurs d'octet

Toutes les valeurs de caractères de \ u0000 à \ u007f (c'est-à-dire de 0 à 127 en décimal)

Toutes les valeurs courtes et entières de -128 à 127.

Développeur Marius Žilėnas
la source
3

Jetez un œil à Integer.java, si la valeur est comprise entre -128 et 127, il utilisera le pool mis en cache, donc (Integer) 1 == (Integer) 1si(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}       
Yanghaogn
la source
0

D'autres réponses décrivent pourquoi les effets observés peuvent être observés, mais c'est vraiment hors de propos pour les programmeurs (intéressant, certes, mais quelque chose que vous devriez tout oublier lors de l'écriture de code réel).

Pour comparer les objets Integer pour l'égalité, utilisez la equalsméthode.

Ne pas essayer de comparer des objets entiers pour l' égalité en utilisant l'opérateur d'identité, ==.

Il peut arriver que certaines valeurs égales soient des objets identiques, mais ce n'est pas quelque chose sur lequel on devrait généralement se fier.

user13463803
la source
-4

J'ai écrit ce qui suit car ce problème n'est pas uniquement spécifique à Integer. Ma conclusion est que le plus souvent, si vous utilisez l'API de manière incorrecte, vous constaterez toujours un comportement incorrect. Utilisez-le correctement et vous devriez voir le bon comportement:

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
thejartender
la source