Java: Inteiro igual a vs ==

152

A partir do Java 1.5, você pode muito bem intercâmbio Integercom intem muitas situações.

No entanto, encontrei um defeito em potencial no meu código que me surpreendeu um pouco.

O código a seguir:

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

parecia estar definindo incorretamente incompatibilidade quando os valores eram iguais, embora não seja possível determinar em que circunstâncias. Eu defini um ponto de interrupção no Eclipse e vi que os Integervalores eram ambos 137, e eu inspecionei a expressão booleana e ela disse que era falsa, mas quando eu a pisei, estava configurando incompatibilidade como true.

Alterando o condicional para:

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

corrigiu o problema.

Alguém pode esclarecer por que isso aconteceu? Até agora, só vi o comportamento no meu host local no meu próprio PC. Nesse caso em particular, o código conseguiu passar por cerca de 20 comparações, mas falhou em 2. O problema era consistentemente reproduzível.

Se for um problema predominante, deve estar causando erros em nossos outros ambientes (dev e test), mas até agora, ninguém relatou o problema após centenas de testes executando esse trecho de código.

Ainda não é legítimo usar ==para comparar dois Integervalores?

Além de todas as boas respostas abaixo, o seguinte link stackoverflow possui algumas informações adicionais. Na verdade, ele teria respondido à minha pergunta original, mas como não mencionei o autoboxing na minha pergunta, ele não apareceu nas sugestões selecionadas:

Por que o compilador / JVM não pode simplesmente fazer com que a caixa automática funcione?

Jeremy Goodell
fonte

Respostas:

238

A JVM está armazenando em cache valores inteiros. == funciona apenas para números entre -128 e 127 http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching

Adão
fonte
1
Obrigado, isso certamente explica por que 137 falham! E também responde à minha pergunta sobre por que não é um problema predominante, em 95% dos casos que vou encontrar, o valor seria inferior a 127. É bom pegar isso agora, embora para os 5% onde não está.
Jeremy Goodell
1
Nota interessante: até algumas semanas atrás, cdiCt e cdsCt eram ints, então isso era bom, mas eu tive que torná-los Inteiros para verificar a situação nula que é tratada de maneira diferente ...
Jeremy Goodell,
3
@ Jeremy Sim, é um problema bastante obscuro, mas como regra geral você usa .equals () para objetos e == para primitivos. Você não pode confiar na caixa automática para teste de igualdade.
Adam
1
Lol, marque de volta para você então! Parece que Colin já tem mais do que pontos suficientes de qualquer maneira.
Jeremy Goodell
2
Observe que novo número inteiro (1)! = Novo número inteiro (1) também. new SEMPRE retorna um novo endereço. A caixa automática usa uma versão em cache. Outras maneiras que retornam números inteiros (sem novos) provavelmente também retornam o valor em cache.
Bill K
77

Você não pode comparar dois Integercom ==objetos simples , para que as referências na maioria das vezes não sejam as mesmas.

Existe um truque: Integerentre -128 e 127, as referências serão as mesmas que os usos de caixa automática Integer.valueOf()que armazenam em cache pequenos números inteiros.

Se o valor p em caixa for verdadeiro, falso, um byte, um caractere no intervalo \ u0000 a \ u007f ou um número int ou pequeno entre -128 e 127, então r1 e r2 serão o resultado de duas conversões de boxe de p. É sempre o caso que r1 == r2.


Recursos :

Sobre o mesmo tópico:

Colin Hebert
fonte
1
A garantia é da JLS ou apenas da Oracle JVM?
Thorbjørn Ravn Andersen
A parte citada é do JLS, por isso é uma garantia do JLS
Colin Hebert
Re: garantia. Eu ainda não confiaria muito nisso. new Integer(1) == new Integer(1)ainda é falso.
Thile
@Thilo new ... == new ...é sempre false.
MC Emperor
2
@ Thilo True, sempre use equals()ao lidar com objetos. Essa deve ser uma das primeiras coisas que se deve saber ao aprender Java. A propósito, eu teria imaginado que o construtor de Integerera privado, ou seja, que as instâncias sempre foram criadas por meio do valueOf()método. Mas vejo que o construtor é público.
MC Emperor
5

O problema é que seus dois objetos Inteiros são apenas isso, objetos. Eles não correspondem porque você está comparando suas duas referências de objeto, não os valores contidos. Obviamente, .equalsé substituído para fornecer uma comparação de valores em oposição a uma comparação de referência de objeto.

MattC
fonte
Boa resposta, mas não explica por que ele só está falhando para 137.
Jeremy Goodell
4

Integerrefere-se à referência, ou seja, ao comparar referências, você está comparando se elas apontam para o mesmo objeto, não para valor. Portanto, o problema que você está vendo. A razão pela qual ele funciona tão bem com inttipos simples é que desmarca o valor contido no Integer.

Posso acrescentar que, se você está fazendo o que está fazendo, por que ter a ifdeclaração para começar?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );
wheaties
fonte
4

"==" sempre compare a localização da memória ou referências a objetos dos valores. O método equals sempre compara os valores. Mas igual também usa indiretamente o operador "==" para comparar os valores.

Inteiro usa o cache inteiro para armazenar os valores de -128 a +127. Se o operador == for usado para verificar se há valores entre -128 e 127, ele retornará true. para outros valores além desses valores, ele retorna false.

Consulte o link para obter informações adicionais

vijay
fonte
1

Além da correção do uso, ==você pode apenas desmarcar um dos Integervalores comparados antes de fazer a ==comparação, como:

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

O segundo será automaticamente retirado da caixa de seleção (é claro que você deve verificar se há nullprimeiro).

Mc Bton
fonte
0

Além dessas ótimas respostas, o que aprendi é que:

NUNCA compare objetos com ==, a menos que você pretenda compará-los por suas referências.

ZhaoGang
fonte