Pourquoi les comparaisons == avec Integer.valueOf (String) donnent des résultats différents pour 127 et 128?

182

Je n'ai aucune idée pourquoi ces lignes de code renvoient des valeurs différentes:

System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));

La sortie est:

true
false
true

Pourquoi le premier revient-il trueet le second revient-il false? Y a-t-il quelque chose de différent que je ne connais pas entre 127et 128? (Bien sûr, je sais que 127< 128.)

Aussi, pourquoi le troisième revient-il true?

J'ai lu la réponse à cette question , mais je n'ai toujours pas compris comment elle peut revenir trueet pourquoi le code de la deuxième ligne revient false.

DnR
la source
6
Integer est un objet; si vous voulez comparer pour l'égalité, utilisez .equals(), sinon tous les paris sont ouverts.
Karl Damgaard Asmussen
6
@KarlDamgaardAsmussen En fait, ici, je veux vraiment tester si ce sont des références au même objet, et au début je ne comprends pas pourquoi 127 128 renvoie un résultat différent.
DnR
@DnR si Java était un langage avec une spécification standardisée, je pense qu'il laisse de telles questions jusqu'à l'implémentation ou même un comportement indéfini obligatoire.
Karl Damgaard Asmussen
1
@jszumski: Cette question ne se limite pas à la simple mise en cache. En outre, la réponse liée est au mieux incomplète - elle n'entre pas tout à fait dans les détails sur ce qui est mis en cache et pourquoi.
Makoto
1
Pour plus de détails sur cette discussion, veuillez vous référer à ce méta-article .
Jeroen Vannevel

Réponses:

191

Il y a une différence frappante ici.

valueOfrenvoie un Integerobjet dont les valeurs peuvent être mises en cache entre -128 et 127. C'est pourquoi la première valeur renvoie true- elle est mise en cache - et la deuxième valeur renvoie false- 128 n'est pas une valeur en cache, vous obtenez donc deux Integerinstances distinctes .

Il est important de noter que vous comparez des références avec Integer#valueOf, et si vous comparez une valeur qui est plus grande que ce que le cache prend en charge, elle ne sera pas évaluée true, même si les valeurs analysées sont équivalentes (cas d'espèce:) Integer.valueOf(128) == Integer.valueOf(128). Vous devez utiliser à la equals()place.

parseIntrenvoie une primitive int. Ceci est la raison pour laquelle la troisième valeur revient true- 128 == 128est évaluée, et bien sûr, true.

Maintenant, il arrive pas mal de faire ce troisième résultat true:

  • Une conversion de déballage se produit en ce qui concerne l'opérateur d'équivalence que vous utilisez et les types de données que vous avez, à savoir, intet Integer. Vous obtenez un Integerdu valueOfcôté droit, bien sûr.

  • Après la conversion, vous comparez deux intvaleurs primitives . La comparaison se produit exactement comme vous vous y attendiez avec les primitives, vous finissez donc par comparer 128et 128.

Makoto
la source
2
@ user3152527: Il y a une différence considérable - l'un est considéré comme un objet, ce qui signifie que vous pouvez appeler des méthodes et interagir avec elle dans des structures de données abstraites, comme List. L'autre est une primitive, qui n'est qu'une valeur brute.
Makoto
1
@ user3152527 Vous avez posé une excellente question (et au pire pas stupide). Mais vous l'avez corrigé pour utiliser .equals, non?
user2910265
3
Ah, il semble que l'interrogateur n'ait pas compris un fait sous-jacent en Java: lorsque vous utilisez "==" pour comparer deux objets, vous testez s'il s'agit de références au même objet. Lorsque vous utilisez "equals ()", vous testez s'ils ont la même valeur. Vous ne pouvez pas utiliser «égal» pour comparer des primitives.
Jay
3
@Jay non, je comprends ça. mais celui qui me déroute au début est pourquoi le premier retourne vrai et le second retourne faux en utilisant la même méthode de comparaison ==. de toute façon, c'est clair maintenant.
DnR
1
Nit: ce n'est pas seulement que Integer "peut" être mis en cache entre -128 et 127. Il doit l' être, selon JLS 5.1.7 . Il peut être mis en cache en dehors de cette plage, mais n'a pas à l'être (et ne l'est souvent pas).
yshavit
127

La Integerclasse a un cache statique, qui stocke 256 Integerobjets spéciaux - un pour chaque valeur entre -128 et 127. Dans cet esprit, considérez la différence entre ces trois.

new Integer(123);

Cela fait (évidemment) un tout nouvel Integerobjet.

Integer.parseInt("123");

Cela renvoie une intvaleur primitive après l'analyse du String.

Integer.valueOf("123");

C'est plus complexe que les autres. Il commence par analyser le fichier String. Ensuite, si la valeur est comprise entre -128 et 127, il renvoie l'objet correspondant à partir du cache statique. Si la valeur est en dehors de cette plage, elle appellenew Integer() et transmet la valeur, de sorte que vous obtenez un nouvel objet.

Maintenant, considérez les trois expressions de la question.

Integer.valueOf("127")==Integer.valueOf("127");

Cela renvoie true, car la Integervaleur dont la valeur est 127 est extraite deux fois du cache statique et comparée à elle-même. Il n'y a qu'un seul Integerobjet impliqué, donc cela revient true.

Integer.valueOf("128")==Integer.valueOf("128");

Cela revient false, car 128 n'est pas dans le cache statique. Un nouveau Integerest donc créé pour chaque côté de l'égalité. Puisqu'il y a deux Integerobjets différents , et ==pour les objets ne renvoie que truesi les deux côtés sont exactement le même objet, cela va être false.

Integer.parseInt("128")==Integer.valueOf("128");

Ceci compare la intvaleur primitive 128 sur la gauche, avec un Integerobjet nouvellement créé sur la droite. Mais comme cela n'a pas de sens de comparer un intà un Integer, Java déballera automatiquement le Integeravant de faire la comparaison; vous finissez donc par comparer un intà un int. Puisque la primitive 128 est égale à elle-même, cela revient true.

Dawood ibn Kareem
la source
13

Prenez soin de renvoyer les valeurs de ces méthodes. La méthode valueOf renvoie une instance Integer:

public static Integer valueOf(int i)

La méthode parseInt renvoie une valeur entière (type primitif):

public static int parseInt(String s) throws NumberFormatException

Explication pour la comparaison:

Afin d'économiser de la mémoire, deux instances des objets wrapper, seront toujours == lorsque leurs valeurs primitives sont les mêmes:

  • Booléen
  • Octet
  • Caractère de \ u0000 à \ u007f (7f est 127 en décimal)
  • Court et entier de -128 à 127

Quand == est utilisé pour comparer une primitive à un wrapper, le wrapper sera déroulé et la comparaison sera primitive à primitive.

Dans votre situation (selon les règles ci-dessus):

Integer.valueOf("127")==Integer.valueOf("127")

Cette expression compare les références au même objet car elle contient une valeur entière comprise entre -128 et 127 et renvoie donc true.

Integer.valueOf("128")==Integer.valueOf("128")

Cette expression compare les références à différents objets car elles contiennent des valeurs entières non comprises dans <-128, 127>, donc elle renvoie false .

Integer.parseInt("128")==Integer.valueOf("128")

Cette expression compare la valeur primitive (côté gauche) et la référence à l'objet (côté droit) afin que le côté droit soit déroulé et son type primitif sera comparé à la gauche afin qu'il retourne true.

piobab
la source
3
Question similaire: stackoverflow.com/questions/9824053/…
piobab
Pouvez-vous fournir une URL pour la source du devis?
Philzen
"... deux instances des objets wrapper, seront toujours == quand leurs valeurs primitives sont les mêmes ..." - absolument faux. Si vous créez deux objets wrapper avec la même valeur, ils ne renverront pas true par rapport à ==, car ce sont des objets différents.
Dawood ibn Kareem
6

Les objets entiers mettent en cache entre -128 et 127 sur 256 Integer

Vous ne devez pas comparer les références d'objet avec == ou ! = . Tu devrais utiliser . équivaut à(..) place, ou mieux - utilisez la primitive int plutôt que Integer.

parseInt : analyse l'argument de chaîne comme un entier décimal signé. Les caractères de la chaîne doivent tous être des chiffres décimaux, sauf que le premier caractère peut être un signe moins ASCII '-' ('\ u002D') pour indiquer une valeur négative. La valeur entière résultante est renvoyée, exactement comme si l'argument et la base 10 étaient donnés comme arguments à la méthode parseInt (java.lang.String, int).

Renvoie un objet Integer contenant la valeur extraite de la chaîne spécifiée lorsqu'elle est analysée avec la base donnée par le deuxième argument. Le premier argument est interprété comme représentant un entier signé dans la base spécifiée par le deuxième argument, exactement comme si les arguments étaient donnés à la méthode parseInt (java.lang.String, int). Le résultat est un objet Integer qui représente la valeur entière spécifiée par la chaîne.

équivalent à

new Integer(Integer.parseInt(s, radix))

radix - la base à utiliser pour interpréter s

donc si vous égalez Integer.valueOf()pour l'entier entre

-128 à 127 il retourne vrai dans votre condition

pour lesser than-128 et greater than127 ça donnefalse

Nambi
la source
6

Pour compléter les réponses données, prenez également note de ce qui suit:

public class Test { 
    public static void main(String... args) { 
        Integer a = new Integer(129);
        Integer b = new Integer(129);
        System.out.println(a == b);
    }
}

Ce code imprimera également: false

Comme l'utilisateur Jay l' a réclamé dans un commentaire pour la réponse acceptée, il faut faire attention lors de l'utilisation d'un opérateur ==sur des objets, ici vous vérifiez si les deux références sont identiques, ce qui n'est pas, car ce sont des objets différents, bien qu'ils représentent même valeur. Pour comparer des objets, vous devez utiliser la equals méthode à la place:

Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));

Cela imprimera: true

Vous pouvez demander, mais alors pourquoi la première ligne imprimée true? . En vérifiant le code source de la Integer.valueOfméthode, vous pouvez voir ce qui suit:

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

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);
}

Si le paramètre est un entier compris entre IntegerCache.low(par défaut -128) et IntegerCache.high(calculé à l'exécution avec une valeur minimale de 127), un objet pré-alloué (mis en cache) est renvoyé. Ainsi, lorsque vous utilisez 127 comme paramètre, vous obtenez deux références au même objet mis en cache et obtenez truela comparaison des références.

Higuaro
la source