¿Por qué las == comparaciones con Integer.valueOf (String) dan resultados diferentes para 127 y 128?

182

No tengo idea de por qué estas líneas de código devuelven 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"));

El resultado es:

true
false
true

¿Por qué regresa el primero truey el segundo false? ¿Hay algo diferente que no sepa entre 127y 128? (Por supuesto que sé que 127< 128.)

Además, ¿por qué regresa el tercero true?

He leído la respuesta a esta pregunta , pero aún no entendí cómo puede regresar truey por qué regresa el código en la segunda línea false.

DnR
fuente
66
El entero es un objeto; si desea comparar por igualdad, use .equals(), de lo contrario todas las apuestas están canceladas.
Karl Damgaard Asmussen
66
@KarlDamgaardAsmussen En realidad, aquí realmente quiero probar si son referencias al mismo objeto, y al principio no entiendo por qué 127 128 devuelven resultados diferentes.
DnR
@DnR si Java fuera un lenguaje con una especificación estandarizada, creo que permitiría que tales asuntos se implementaran o incluso que tuvieran un comportamiento indefinido obligatorio.
Karl Damgaard Asmussen
1
@jszumski: Sin embargo, hay más en esta pregunta que solo la porción de almacenamiento en caché. Además, la respuesta vinculada es incompleta en el mejor de los casos: no entra en detalles en cuanto a qué está almacenado en caché y por qué.
Makoto
1
Para un mayor seguimiento de esta discusión, consulte esta meta publicación .
Jeroen Vannevel

Respuestas:

191

Hay una notable diferencia aquí.

valueOfestá devolviendo un Integerobjeto, que puede tener sus valores almacenados en caché entre -128 y 127. Es por eso que el primer valor regresa true- está almacenado en caché - y el segundo valor devuelve false- 128 no es un valor almacenado en caché, por lo que está obteniendo dos Integerinstancias separadas .

Es importante tener en cuenta que usted está comparando con referencias Integer#valueOf, y si usted está comparando un valor que es mayor de lo que los soportes de memoria caché, será no evaluar a true, incluso si los valores analizados son equivalentes (ejemplo de ello: Integer.valueOf(128) == Integer.valueOf(128)). Usted debe utilizar equals()en su lugar.

parseIntestá devolviendo un primitivo int. Es por eso que el tercer valor devuelve true: 128 == 128se evalúa y, por supuesto, es true.

Ahora, sucede un poco para lograr ese tercer resultado true:

  • Se produce una conversión de unboxing con respecto al operador de equivalencia que está utilizando y los tipos de datos que tiene, a saber, inty Integer. Obtendrá una Integerdel valueOflado derecho, por supuesto.

  • Después de la conversión, estás comparando dos intvalores primitivos . La comparación se realiza tal como cabría esperar con respecto a las primitivas, por lo que terminas comparando 128y 128.

Makoto
fuente
2
@ user3152527: Hay una diferencia considerable: uno se considera un objeto, lo que significa que puede llamar a métodos e interactuar con él en estructuras de datos abstractas, como List. El otro es un primitivo, que es solo un valor bruto.
Makoto
1
@ user3152527 Hiciste una excelente pregunta (y no una tonta en el peor de los casos). Pero lo has arreglado para usar .equals, ¿verdad?
user2910265
3
Ah, parece que el interlocutor no entendió un hecho subyacente en Java: al usar "==" para comparar dos objetos, está probando si son referencias al mismo objeto. Cuando usa "equals ()", está probando si tienen el mismo valor. No puede usar "iguales" para comparar primitivas.
Jay
3
@ Jay, no, lo entiendo. pero el que me confunde al principio es por qué el primero devuelve verdadero y el segundo devuelve falso usando el mismo método de comparación ==. de todos modos, está claro ahora.
DnR
1
Nit: no se trata solo de que Integer "puede" almacenarse en caché entre -128 y 127. Debe serlo, de acuerdo con JLS 5.1.7 . Se puede ser almacenado en caché fuera de ese rango, pero no tiene que ser (y, a menudo no lo es).
yshavit
127

La Integerclase tiene un caché estático, que almacena 256 Integerobjetos especiales , uno para cada valor entre -128 y 127. Con eso en mente, considere la diferencia entre estos tres.

new Integer(123);

Esto (obviamente) hace un nuevo Integerobjeto.

Integer.parseInt("123");

Esto devuelve un intvalor primitivo después de analizar el String.

Integer.valueOf("123");

Esto es más complejo que los demás. Comienza analizando el String. Luego, si el valor está entre -128 y 127, devuelve el objeto correspondiente de la caché estática. Si el valor está fuera de este rango, invoca new Integer()y pasa el valor, para que obtenga un nuevo objeto.

Ahora, considere las tres expresiones en la pregunta.

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

Esto devuelve verdadero, porque Integercuyo valor es 127 se recupera dos veces del caché estático y se compara con sí mismo. Solo hay un Integerobjeto involucrado, así que esto regresa true.

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

Esto vuelve false, porque 128 no está en el caché estático. Entonces Integerse crea una nueva para cada lado de la igualdad. Dado que hay dos Integerobjetos diferentes , y ==para los objetos solo se devuelve truesi ambos lados son exactamente el mismo objeto, esto será false.

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

Esto es comparar el intvalor primitivo 128 a la izquierda, con un Integerobjeto recién creado a la derecha. Pero debido a que no tiene sentido comparar un intan Integer, un Java desempaquetará automáticamente el Integerantes de hacer la comparación; entonces terminas comparando una intcon una int. Como el primitivo 128 es igual a sí mismo, esto vuelve true.

Dawood ibn Kareem
fuente
13

Tenga cuidado de devolver los valores de estos métodos. El método valueOf devuelve una instancia de Integer:

public static Integer valueOf(int i)

El método parseInt devuelve un valor entero (tipo primitivo):

public static int parseInt(String s) throws NumberFormatException

Explicación para la comparación:

Para ahorrar memoria, dos instancias de los objetos de envoltura siempre serán == cuando sus valores primitivos son los mismos:

  • Booleano
  • Byte
  • Carácter de \ u0000 a \ u007f (7f es 127 en decimal)
  • Corto e entero de -128 a 127

Cuando == se usa para comparar una primitiva con una envoltura, la envoltura se desenvolverá y la comparación será primitiva con primitiva.

En su situación (de acuerdo con las reglas anteriores):

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

Esta expresión compara referencias al mismo objeto porque contiene un valor entero entre -128 y 127, por lo que regresa true.

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

Esta expresión compara referencias a diferentes objetos porque contienen valores enteros que no están en <-128, 127> por lo que devuelve false.

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

Esta expresión compara el valor primitivo (lado izquierdo) y la referencia al objeto (lado derecho) para que el lado derecho se desenvuelva y su tipo primitivo se compare con el izquierdo para que regrese true.

piobab
fuente
3
Pregunta similar: stackoverflow.com/questions/9824053/…
piobab
¿Puede proporcionar una URL para la fuente de cotización?
Philzen
"... dos instancias de los objetos wrapper, siempre serán == cuando sus valores primitivos son los mismos ..." - absolutamente falso. Si crea dos objetos de contenedor con el mismo valor, no devolverán verdadero en comparación con ==, porque son objetos diferentes.
Dawood ibn Kareem
6

Los objetos enteros se almacenan en caché entre -128 y 127 de 256 enteros

No debe comparar referencias de objetos con == o ! = . Deberías usar . igual (..) en su lugar, o mejor - use el primitivo int en lugar de Integer.

parseInt : analiza el argumento de cadena como un entero decimal con signo. Todos los caracteres de la cadena deben ser dígitos decimales, excepto que el primer carácter puede ser un signo menos ASCII '-' ('\ u002D') para indicar un valor negativo. Se devuelve el valor entero resultante, exactamente como si el argumento y la raíz 10 fueran dados como argumentos al método parseInt (java.lang.String, int).

valueOf Devuelve un objeto Entero que contiene el valor extraído de la Cadena especificada cuando se analiza con la raíz dada por el segundo argumento. El primer argumento se interpreta como la representación de un entero con signo en la raíz especificada por el segundo argumento, exactamente como si los argumentos se hubieran dado al método parseInt (java.lang.String, int). El resultado es un objeto entero que representa el valor entero especificado por la cadena.

equivalente a

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

radix: la raíz que se utilizará para interpretar s

así que si eres igual Integer.valueOf()para el número entero entre

-128 a 127 vuelve verdadero en su condición

para lesser than-128 y greater than127 dafalse

Nambi
fuente
6

Para complementar las respuestas dadas, también tenga en cuenta lo siguiente:

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 también imprimirá: false

Como el usuario Jay ha afirmado en un comentario para la respuesta aceptada, se debe tener cuidado al usar el operador ==en los objetos, aquí está comprobando si ambas referencias son iguales, lo que no es así, porque son objetos diferentes, aunque representan el mismo mismo valor Para comparar objetos, debe usar el equals método en su lugar:

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

Esto imprimirá: true

Usted puede preguntar, pero entonces ¿por qué se imprime la primera línea true? . Verificando el código fuente del Integer.valueOfmétodo, puede ver lo siguiente:

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 el parámetro es un número entero entre IntegerCache.low(predeterminado en -128) y IntegerCache.high(calculado en tiempo de ejecución con el valor mínimo 127), se devuelve un objeto preasignado (en caché). Entonces, cuando usa 127 como parámetro, obtiene dos referencias al mismo objeto en caché y obtiene truela comparación de las referencias.

higuaro
fuente