programacion - Usar el operador== en Java para comparar objetos de envoltura
operadores relacionales en java (8)
Estoy leyendo SCJP Java 6 por Kathy Sierra y Bert Bates y este libro me confunde mucho. En la página 245 indican que el siguiente código a continuación.
Integer i1 = 1000;
Integer i2 = 1000;
if(i1 != i2)
System.out.println("different objects");
//Prints output
different objects
Luego en la página siguiente tienen el siguiente código
Integer i3 = 10;
Integer i4 = 10;
if(i3 == i4)
System.out.println("same objects");
//Prints output
same objects
¡Estoy tan confundida! Cuando pruebo esto por mi cuenta, parece que no puedes usar el == para comparar de la misma manera que usarías el método equals (). El uso de == siempre me da ''falso'' incluso si las variables enteras están configuradas con el mismo valor (es decir, 10). ¿Estoy en lo correcto? Usar el == para comparar el mismo objeto entero (con los mismos valores) siempre dará como resultado ''falso''
"==" siempre compara la ubicación de la memoria o las referencias a objetos de los valores. El método equals siempre compara los valores. Pero equals también usa indirectamente el operador "==" para comparar los valores. Entero utiliza caché de enteros para almacenar los valores de -128 a + 127. Si el operador == se utiliza para verificar cualquier valor entre -128 a 127, entonces devuelve verdadero. si cualquier valor entre -128 a 127 como
Integer i1 = -128;
Integer i2 = -128;
System.out.println(i1 == i2); // returns true
que no sea el rango anterior, entonces devuelve falso
Integer i1 = 1000;
Integer i2 = 1000;
System.out.println(i1 == i2); // returns false
Consulte el enlace para obtener información adicional
Cuando el operador Java == se usa para comparar cualquier cosa que no sean tipos primitivos, verifica la igualdad referencial; esto se aplica incluso cuando las cosas que se comparan son primitivas envueltas. Además, el método valueOf
y la declaración de autoboxing generada por el compilador generalmente son libres de devolver arbitrariamente un nuevo objeto que no será de referencia igual a cualquier otra referencia previamente existente, o de devolver una referencia a un objeto existente (que, por supuesto, , ser referencia-igual a cualquier referencia preexistente que identifique el mismo objeto). Las implementaciones son necesarias para mantener un "grupo" de instancias de Integer
para los valores -128 a 127, de modo que todas las llamadas a Integer.valueOf
en cualquier número particular dentro de ese rango devolverán referencias al mismo objeto, pero aparte de eso una implementación sería libre de hacer algo como
static Integer [] intPool = new Integer[256];
public Integer valueOf(int n)
{
int hash = (n*0x18675309) >>> 24;
Integer instance = intPool[n];
if (instance == null && instance.value != n)
{
instance = new Integer(n);
intPool[hash] = instance ;
}
return instance;
}
En particular, no espero que las implementaciones de Java hagan algo así, ya que en muchos casos la proporción de "caché de aciertos" podría ser cercana al 0% y se desperdiciaría el tiempo extra dedicado a buscar instancias en el caché. Sin embargo, nunca hay ninguna garantía de que una referencia devuelta por instanceOf
no coincida con alguna referencia anterior devuelta por ese método (incluso si no coincide con la última referencia devuelta por ese método, algunos algoritmos de almacenamiento en caché posiblemente podrían hacer que devuelva un referencia anterior , especialmente si el conjunto está compartido por varios subprocesos sin bloqueo. La falta de bloqueo nunca hará que el código devuelva nada más que una referencia a un entero con el valor correcto, pero podría causar variaciones impredecibles en las que las referencias devueltas se igualen ) Solo se garantiza que la única referencia a los objetos Integer
creados directamente usando el constructor new Integer(n)
es único; código que espera que cualquier referencia devuelta por valueOf
para no coincidir con cualquier referencia devuelta por valueOf
, sin haber observado realmente que no coincida, se considere desglosada.
De acuerdo con jls-5.1.7
If the value p being boxed is true, false, a byte, or a char in the range /u0000 to /u007f,
or an int or short number between -128 and 127 (inclusive), then let r1 and r2
be the results of any two boxing conversions of p. It is always the case that r1 == r2.
Entonces, cualquier número entre -128 y 127 es guardado en caché por la clase Interger.
Recuerde, al comparar dos objetos siempre use el método equals
.
El código de almacenamiento en caché está escrito en la clase IntegerCache
que es miembro de la clase Integer
.
Aquí está el fragmento de código:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
Referencias
La clave de la respuesta se llama internamiento de objeto . Java interviene en números pequeños (menos de 128), por lo que todas las instancias de Integer(n)
con n
en el rango interno son las mismas. Los números mayores que o igual a 128 no son internados, por lo tanto, los objetos Integer(1000)
no son iguales entre sí.
La comparación de cadenas y la comparación de enteros utilizando == y! = Da resultados booleanos no como esperamos. Tenga cuidado y asegúrese de que los posibles resultados desconocidos no impidan el rendimiento, la confiabilidad y la precisión de su software.
La respuesta anterior sobre Interning es correcta. Algo a considerar aunque si lo haces:
Integer i3 = new Integer(10);
Integer i4 = new Integer(10);
No tendrá los objetos nuevos ya que ha creado objetos nuevos explícitamente. Si escribe el código de la siguiente manera, será internado:
Integer i3 = Integer.valueOf(10);
Integer i4 = Integer.valueOf(10);
Ahora serán el mismo objeto nuevamente. Si echas un vistazo al valueOf Method dentro de la clase Integer.java en el archivo src.zip, puedes ver dónde verifica si el valor de int está fuera de -128 a 127. De lo contrario, llama a la nueva clase Integer. lo carga desde el caché.
Si observa el código fuente para Integer
, verá que Integer.valueOf(int)
agrupa todos los valores -128 a 127. El motivo es que los valores enteros pequeños se utilizan con frecuencia y, por lo tanto, son dignos de agruparse / almacenarse en caché.
Tomado directamente de Integer.java
:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
Tenga en cuenta que este agrupamiento es específico de la implementación y no hay garantía del rango agrupado.
Las respuestas sobre el internamiento son correctas en concepto, pero incorrectas con la terminología. El internamiento en Java normalmente implica que el tiempo de ejecución de Java está realizando el agrupamiento (como el interno de String). En el caso de Integer, es la clase en sí la que está haciendo la agrupación. No hay magia JVM involucrada.
Integer i1 = 1000;
Integer i2 = 1000;
El compilador ''encajona'' el int 1000 como objeto Integer. Para hacer esto, convierte la fuente a lo siguiente:
Integer i1 = Integer.valueOf(1000);
Integer i2 = Integer.valueOf(1000);
Ahora valueOf
podría ser una simple llamada a new Integer(1000)
sin embargo, crear un nuevo objeto Integer cada vez que se int
una int
costaría tiempo y espacio. Para evitar esto, la clase Integer mantiene una matriz de objetos Integer para un rango limitado de valores int.
if(value> maxRange || value< minRange){
//not in pool return new Integer
return new Integer(value);
}else{
//return pooled Integer object
//for the value, pool contains all Integer
//values from minRange to maxRange
return integerPool[value-minRange];
}
La velocidad ganada frente a la memoria perdida se puede ajustar estableciendo el rango con un argumento jvm al inicio del programa (afaik se predetermina a -127 a 128).