Java: nombre entier égal à ==

152

À partir de Java 1.5, vous pouvez pratiquement échanger Integeravec intdans de nombreuses situations.

Cependant, j'ai trouvé un défaut potentiel dans mon code qui m'a un peu surpris.

Le code suivant:

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

semblait définir incorrectement une incompatibilité lorsque les valeurs étaient égales, bien que je ne puisse pas déterminer dans quelles circonstances. J'ai défini un point d'arrêt dans Eclipse et j'ai vu que les Integervaleurs étaient toutes les deux de 137, et j'ai inspecté l'expression booléenne et il a dit qu'elle était fausse, mais quand je l'ai enjambée, elle définissait l'incohérence sur vrai.

Changer le conditionnel en:

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

résolu le problème.

Quelqu'un peut-il expliquer pourquoi cela s'est produit? Jusqu'à présent, je n'ai vu le comportement de mon hôte local que sur mon propre PC. Dans ce cas particulier, le code a réussi à dépasser environ 20 comparaisons, mais a échoué sur 2. Le problème était toujours reproductible.

S'il s'agit d'un problème répandu, il devrait causer des erreurs sur nos autres environnements (développement et test), mais jusqu'à présent, personne n'a signalé le problème après des centaines de tests exécutant cet extrait de code.

N'est-il toujours pas légitime d'utiliser ==pour comparer deux Integervaleurs?

En plus de toutes les bonnes réponses ci-dessous, le lien stackoverflow suivant contient un peu d'informations supplémentaires. Cela aurait en fait répondu à ma question initiale, mais comme je n'ai pas mentionné la mise en boîte automatique dans ma question, il n'apparaît pas dans les suggestions sélectionnées:

Pourquoi le compilateur / JVM ne peut-il pas simplement faire fonctionner l'autoboxing?

Jeremy Goodell
la source

Réponses:

238

La machine virtuelle Java met en cache les valeurs entières. == ne fonctionne que pour les nombres compris entre -128 et 127 http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching

Adam
la source
1
Merci, cela explique certainement pourquoi 137 échoue! Et cela répond aussi à ma question sur la raison pour laquelle ce n'est pas un problème répandu, dans 95% des cas que je vais rencontrer, la valeur serait inférieure à 127. Bon à saisir maintenant, mais pour les 5% où ce n'est pas le cas.
Jeremy Goodell
1
Note latérale intéressante: jusqu'à il y a quelques semaines, cdiCt et cdsCt étaient tous les deux des entiers, donc c'était bien, mais j'ai dû les faire des entiers afin de vérifier la situation nulle qui est gérée différemment ...
Jeremy Goodell
3
@Jeremy Oui, c'est un problème assez obscur, mais en règle générale, vous utilisez .equals () pour les objets et == pour les primitives. Vous ne pouvez pas compter sur l'autounboxing pour les tests d'égalité.
Adam le
1
Lol, coche à toi alors! On dirait que Colin a déjà assez de points de toute façon.
Jeremy Goodell
2
Notez que new Integer (1)! = New Integer (1) également. new renvoie TOUJOURS une nouvelle adresse. La boxe automatique utilise une version mise en cache. D'autres moyens qui retournent des entiers (sans les nouveaux) renvoient probablement également la valeur mise en cache.
Bill K
77

Vous ne pouvez pas comparer deux Integeravec un simple ==objet, donc la plupart du temps, les références ne seront pas les mêmes.

Il y a une astuce, avec Integerentre -128 et 127, les références seront les mêmes que les utilisations de l'autoboxing Integer.valueOf()qui met en cache les petits entiers.

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.


Ressources :

Sur le même sujet:

Colin Hebert
la source
1
Est-ce que la garantie vient du JLS ou uniquement de la JVM Oracle?
Thorbjørn Ravn Andersen
La partie citée provient de la JLS, c'est donc une garantie de la JLS
Colin Hebert
Re: garantie. Je ne m'y fierais toujours pas trop. new Integer(1) == new Integer(1)est toujours faux.
Thilo
@Thilo l' new ... == new ...est toujours false.
MC Emperor
2
@Thilo True, utilisez toujours equals()lorsque vous traitez avec des objets. Cela devrait être l'une des premières choses à savoir lors de l'apprentissage de Java. Au fait, j'aurais deviné que le constructeur de Integerétait privé, c'est-à-dire que les instances étaient toujours créées via la valueOf()méthode. Mais je vois que le constructeur est public.
MC Emperor
5

Le problème est que vos deux objets Integer ne sont que des objets. Ils ne correspondent pas car vous comparez vos deux références d'objet, pas les valeurs qu'elles contiennent. Il .equalsest évidemment remplacé pour fournir une comparaison de valeur par opposition à une comparaison de référence d'objet.

MattC
la source
Bonne réponse, mais cela n'explique pas pourquoi il échoue seulement pour 137.
Jeremy Goodell
4

Integerfait référence à la référence, c'est-à-dire lorsque vous comparez des références que vous comparez si elles pointent vers le même objet, pas vers la valeur. Par conséquent, le problème que vous voyez. La raison pour laquelle cela fonctionne si bien avec plainint types est qu'il déballe la valeur contenue par le Integer.

Puis-je ajouter que si vous faites ce que vous faites, pourquoi ifcommencer par cette déclaration?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );
Wheaties
la source
4

"==" compare toujours l'emplacement mémoire ou les références d'objet des valeurs. La méthode equals compare toujours les valeurs. Mais equals utilise aussi indirectement l'opérateur "==" pour comparer les valeurs.

Integer utilise le cache Integer pour stocker les valeurs de -128 à +127. Si l'opérateur == est utilisé pour vérifier les valeurs comprises entre -128 et 127, il renvoie true. pour les autres valeurs, il renvoie false.

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

vijay
la source
1

De plus, pour l'exactitude de l'utilisation, ==vous pouvez simplement déballer l'une des Integervaleurs comparées avant de faire la ==comparaison, comme:

if ( firstInteger.intValue() == secondInteger ) {..

Le second sera automatiquement déballé (bien sûr, vous devez d'abord vérifier nulls).

Mc Bton
la source
0

Outre ces bonnes réponses, ce que j'ai appris, c'est que:

NE JAMAIS comparer des objets avec == à moins que vous n'ayez l'intention de les comparer par leurs références.

ZhaoGang
la source