Por que == comparações com Integer.valueOf (String) fornecem resultados diferentes para 127 e 128?


182

Não faço ideia por que essas linhas de código retornam valores diferentes:

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

A saída é:

true
false
true

Por que o primeiro retorna truee o segundo retorna false? Existe algo diferente que eu não conheça 127e 128? (Claro que eu sei que 127< 128.)

Além disso, por que o terceiro retorna true?

Eu li a resposta desta pergunta , mas ainda não entendi como ela pode retornar truee por que o código na segunda linha retorna false.


6
Inteiro é um objeto; se você deseja comparar a igualdade, use .equals(), caso contrário, todas as apostas estão desativadas.
Karl Damgaard Asmussen

6
@KarlDamgaardAsmussen Na verdade, aqui realmente quero testar se são referências ao mesmo objeto e, a princípio, não entendo por que 127 128 retornam resultados diferentes.
DnR

@DnR se Java fosse uma linguagem com uma especificação padronizada, eu acho que isso deixaria essas questões em ordem de execução ou até mesmo comportamento indefinido.
Karl Damgaard Asmussen

1
@jszumski: Há mais nessa questão do que apenas a parte do cache, no entanto. Além disso, a resposta vinculada é incompleta na melhor das hipóteses - ela não entra em detalhes quanto ao que é armazenado em cache e por quê.
Makoto

1
Para um acompanhamento mais aprofundado dessa discussão, consulte esta meta post .
Jeroen Vannevel

Respostas:


191

Há uma diferença marcante aqui.

valueOfestá retornando um Integerobjeto, que pode ter seus valores em cache entre -128 e 127. É por isso que o primeiro valor retorna true- ele é armazenado em cache - e o segundo valor retorna false- 128 não é um valor armazenado em cache, então você está recebendo duas Integerinstâncias separadas .

É importante observar que você está comparando referências e Integer#valueOf, se estiver comparando um valor maior do que o cache suporta, ele não será avaliado true, mesmo que os valores analisados ​​sejam equivalentes (caso em questão Integer.valueOf(128) == Integer.valueOf(128):). Você deve usar em seu equals()lugar.

parseIntestá retornando um primitivo int. É por isso que o terceiro valor retorna true- 128 == 128é avaliado e, é claro true,.

Agora, acontece um pouco para obter esse terceiro resultado true:

  • Uma conversão de unboxing ocorre com relação ao operador de equivalência que você está usando e os tipos de dados que você possui - ou seja, inte Integer. Você está recebendo um Integerdo valueOflado direito, é claro.

  • Após a conversão, você está comparando dois intvalores primitivos . A comparação acontece exatamente como seria de esperar em relação às primitivas, então você acaba comparando 128e 128.


2
@ user3152527: Há uma diferença considerável: uma é considerada um objeto, o que significa que você pode chamar métodos e interagir com ele em estruturas de dados abstratas, como List. O outro é um primitivo, que é apenas um valor bruto.
Makoto

1
@ user3152527 Você fez uma excelente pergunta (e não uma idiota na pior das hipóteses). Mas você o corrigiu para usar .equals, certo?
precisa saber é o seguinte

3
Ah, parece que o questionador não entendeu um fato subjacente em Java: Ao usar "==" para comparar dois objetos, você está testando se eles são referências ao mesmo objeto. Ao usar "equals ()", você está testando se eles têm o mesmo valor. Você não pode usar "iguais" para comparar primitivas.
Jay

3
@ Jay não, eu entendo isso. mas o que me confundiu no início é o motivo pelo qual o primeiro retorna true e o segundo retorna false usando o mesmo método de comparação ==. de qualquer maneira, está claro agora.
DnR

1
Nit: não é apenas que o inteiro "possa" ser armazenado em cache entre -128 e 127. Deve ser, de acordo com o JLS 5.1.7 . ele pode ser armazenado em cache fora desse intervalo, mas não tem que ser (e muitas vezes não é).
usar o seguinte comando

127

A Integerclasse possui um cache estático, que armazena 256 Integerobjetos especiais - um para cada valor entre -128 e 127. Com isso em mente, considere a diferença entre esses três.

new Integer(123);

Isso (obviamente) cria um novo Integerobjeto.

Integer.parseInt("123");

Isso retorna um intvalor primitivo após analisar o String.

Integer.valueOf("123");

Isso é mais complexo que os outros. Começa analisando o arquivo String. Então, se o valor estiver entre -128 e 127, ele retornará o objeto correspondente do cache estático. Se o valor estiver fora desse intervalo, ele chamará new Integer()e passará o valor, para que você obtenha um novo objeto.

Agora, considere as três expressões na pergunta.

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

Isso retorna true, porque o Integervalor cujo é 127 é recuperado duas vezes do cache estático e comparado a si mesmo. Há apenas um Integerobjeto envolvido, então isso retorna true.

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

Isso retorna false , porque 128 não está no cache estático. Portanto, um novo Integeré criado para cada lado da igualdade. Como existem dois Integerobjetos diferentes , e ==para os objetos retornará apenas truese os dois lados forem exatamente o mesmo objeto, será esse o caso false.

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

Isso está comparando o intvalor primitivo 128 à esquerda, com um Integerobjeto recém-criado à direita. Mas, como não faz sentido comparar um intcom um Integer, o Java descompactará automaticamente o Integerantes de fazer a comparação; então você acaba comparando umint para um int. Como o primitivo 128 é igual a si mesmo, isso retorna true.


13

Cuide para retornar valores desses métodos. O método valueOf retorna uma instância Integer:

public static Integer valueOf(int i)

O método parseInt retorna um valor inteiro (tipo primitivo):

public static int parseInt(String s) throws NumberFormatException

Explicação para comparação:

Para economizar memória, duas instâncias dos objetos do invólucro sempre serão == quando seus valores primitivos forem os mesmos:

  • boleano
  • Byte
  • Caractere de \ u0000 a \ u007f (7f é 127 em decimal)
  • Curto e Inteiro de -128 a 127

Quando == é usado para comparar um primitivo a um wrapper, o wrapper será desembrulhado e a comparação será primitiva para primitiva.

Na sua situação (de acordo com as regras acima):

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

Essa expressão compara referências ao mesmo objeto, pois contém o valor Inteiro entre -128 e 127 e, portanto, retorna true.

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

Esta expressão compara referências a objetos diferentes porque eles contêm valores Inteiros que não estão em <-128, 127> e, portanto, retornam false.

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

Essa expressão compara o valor primitivo (lado esquerdo) e a referência ao objeto (lado direito), para que o lado direito seja desembrulhado e seu tipo primitivo seja comparado ao esquerdo, para que ele retorne true.



Você pode fornecer um URL para a fonte da cotação?
Philzen 25/03

"... duas instâncias dos objetos do wrapper sempre serão == quando seus valores primitivos forem os mesmos ..." - absolutamente falso. Se você criar dois objetos wrapper com o mesmo valor, eles não retornarão true quando comparados a ==, porque são objetos diferentes.
Dawood ibn Kareem

6

Objetos inteiros fazem cache entre -128 e 127 de 256 Inteiro

Você não deve comparar referências de objeto com == ou ! = . Você deveria usar . é igual a(..) vez disso, ou melhor - use o primitivo int em vez de Inteiro.

parseInt : analisa o argumento da string como um número inteiro decimal assinado. Os caracteres na sequência devem ser todos dígitos decimais, exceto que o primeiro caractere pode ser um sinal de menos ASCII '-' ('\ u002D') para indicar um valor negativo. O valor inteiro resultante é retornado, exatamente como se o argumento e a raiz 10 fossem fornecidos como argumentos para o método parseInt (java.lang.String, int).

valueOf Retorna um objeto Inteiro que mantém o valor extraído da String especificada quando analisado com a raiz fornecida pelo segundo argumento. O primeiro argumento é interpretado como representando um número inteiro assinado na raiz especificada pelo segundo argumento, exatamente como se os argumentos fossem fornecidos ao método parseInt (java.lang.String, int). O resultado é um objeto Inteiro que representa o valor inteiro especificado pela sequência.

equivalente a

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

radix - o radical a ser usado na interpretação de s

então se você igualar Integer.valueOf()para o número inteiro entre

-128 a 127 retorna verdadeiro em sua condição

para lesser than-128 e greater than127 dáfalse


6

Para complementar as respostas fornecidas, observe também o seguinte:

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

Este código também imprimirá: false

Como o usuário Jay reivindicou em um comentário a resposta aceita, deve-se tomar cuidado ao usar o operador ==em objetos, aqui você está verificando se as duas referências são iguais, o que não é, porque são objetos diferentes, embora representem o mesmo valor. Para comparar objetos, você deve usar o equals método:

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

Isso imprimirá: true

Você pode perguntar: mas então por que a primeira linha foi impressa true? . Verificando o código fonte do Integer.valueOfmétodo, você pode ver o seguinte:

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

Se o parâmetro for um número inteiro entre IntegerCache.low(padrão -128) e IntegerCache.high(calculado em tempo de execução com valor mínimo 127), um objeto pré-alocado (em cache) será retornado. Portanto, quando você usa 127 como parâmetro, obtém duas referências ao mesmo objeto em cache e faz truea comparação das referências.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.