¿Cómo comparar correctamente dos enteros en Java?

Sé que si comparas un Entero primitivo encuadrado con una constante como:

Integer a = 4; if (a < 5) 

a se desempaquetará automáticamente y la comparación funcionará.

Sin embargo, ¿qué sucede cuando se comparan dos Integers y se desea comparar la igualdad o menos de / mayor que?

 Integer a = 4; Integer b = 5; if (a == b) 

¿El resultado del código anterior será verificar si son el mismo objeto, o será unbox automático en ese caso?

Qué pasa:

 Integer a = 4; Integer b = 5; if (a < b) 

?

No, == entre Entero, Largo, etc. verificará la igualdad de referencia , es decir,

 Integer x = ...; Integer y = ...; System.out.println(x == y); 

esto verificará si y refieren al mismo objeto en lugar de objetos iguales .

Asi que

 Integer x = new Integer(10); Integer y = new Integer(10); System.out.println(x == y); 

está garantizado para imprimir false . El internamiento de valores autoboxados “pequeños” puede conducir a resultados complicados:

 Integer x = 10; Integer y = 10; System.out.println(x == y); 

Esto se imprimirá true , debido a las reglas del boxeo ( JLS sección 5.1.7 ). Todavía se usa la igualdad de referencia, pero las referencias son genuinamente iguales.

Personalmente usaría:

 if (x.intValue() == y.intValue()) 

o

 if (x.equals(y)) 

Este último es ligeramente menos eficiente – no hay una sobrecarga para Integer.equals(Integer) por lo que tendrá que hacer la verificación de tipo de tiempo de ejecución, mientras que el primero usa el hecho de que ya sabemos que ambos objetos son Integer s.

Afortunadamente, compareTo sabe sobre los tipos, entonces:

 if (x.compareTo(y) < 0) 

debería ser eficiente. Por supuesto, este es un territorio de micro-optimización y debes usar el código que encuentres más claro, después de asegurarte de que sea correcto 🙂

Como dices, para cualquier comparación entre un tipo de envoltura ( Integer , Long , etc.) y un tipo numérico ( int , long , etc.), el valor del tipo de envoltura se desempaqueta y la prueba se aplica a los valores primitivos involucrados.

Esto ocurre como parte de la promoción numérica binaria ( sección 5.6.2 de JLS ). Mire la documentación de cada operador individual para ver si se aplica. Por ejemplo, de los documentos para == y! = ( JLS 15.21.1 ):

Si los operandos de un operador de igualdad son ambos de tipo numérico, o uno es de tipo numérico y el otro es convertible (§5.1.8) a tipo numérico, se realiza una promoción numérica binaria en los operandos (§5.6.2).

y para <, <=,> y> = ( JLS 15.20.1 )

El tipo de cada uno de los operandos de un operador de comparación numérica debe ser un tipo que sea convertible (§5.1.8) a un tipo numérico primitivo, o que ocurra un error en tiempo de comstackción. La promoción numérica binaria se realiza en los operandos (§5.6.2). Si el tipo promocionado de los operandos es int o largo, entonces se realiza una comparación de entero con signo; si este tipo promocionado es flotante o doble, se realiza una comparación de coma flotante.

Observe cómo nada de esto se considera parte de la situación donde ninguno de los tipos es un tipo numérico.

== aún probará la igualdad de objetos. Sin embargo, es fácil ser engañado:

 Integer a = 10; Integer b = 10; System.out.println(a == b); //prints true Integer c = new Integer(10); Integer d = new Integer(10); System.out.println(c == d); //prints false 

Sus ejemplos con desigualdades funcionarán ya que no están definidos en Objetos. Sin embargo, con la comparación == , la igualdad de objetos seguirá siendo verificada. En este caso, cuando inicializa los objetos de una primitiva en caja, se usa el mismo objeto (para a y b). Esta es una buena optimización ya que las clases primitivas de caja son inmutables.

== comprueba la igualdad de referencia, sin embargo, al escribir código como:

 Integer a = 1; Integer b = 1; 

Java es lo suficientemente inteligente como para reutilizar el mismo inmutable para a y b , así que esto es cierto: a == b . Curioso, escribí un pequeño ejemplo para mostrar dónde java deja de optimizar de esta manera:

 public class BoxingLol { public static void main(String[] args) { for (int i = 0; i < Integer.MAX_VALUE; i++) { Integer a = i; Integer b = i; if (a != b) { System.out.println("Done: " + i); System.exit(0); } } System.out.println("Done, all values equal"); } } 

Cuando compilo y ejecuto esto (en mi máquina), obtengo:

 Done: 128 

Vocación

 if (a == b) 

Funcionará la mayor parte del tiempo, pero no se garantiza que siempre funcione, así que no lo use.

La forma más adecuada de comparar dos clases de enteros para la igualdad, suponiendo que se llamen ‘a’ y ‘b’ es llamar:

 if(a != null && a.equals(b)) { System.out.println("They are equal"); } 

También puede usar de esta manera, que es un poco más rápido.

  if(a != null && b != null && (a.intValue() == b.intValue())) { System.out.println("They are equal"); } 

En mi máquina, 99 mil millones de operaciones tomaron 47 segundos con el primer método y 46 segundos con el segundo. Debería estar comparando miles de millones de valores para ver cualquier diferencia.

Tenga en cuenta que ‘a’ puede ser nulo, ya que es un Objeto. Comparando de esta manera no causará una excepción de puntero nulo.

Para comparar mayor y menor, use

 if (a != null && b!=null) { int compareValue = a.compareTo(b); if (compareValue > 0) { System.out.println("a is greater than b"); } else if (compareValue < 0) { System.out.println("b is greater than a"); } else { System.out.println("a and b are equal"); } } else { System.out.println("a or b is null, cannot compare"); } 

Desde Java 1.7 puedes usar Objects.equals :

 java.util.Objects.equals(oneInteger, anotherInteger); 

Devuelve verdadero si los argumentos son iguales entre sí y falso de lo contrario. En consecuencia, si ambos argumentos son nulos, se devuelve verdadero y si exactamente un argumento es nulo, se devuelve falso. De lo contrario, la igualdad se determina utilizando el método equals del primer argumento.

tl; dr mi opinión es usar unario + para desencadenar el desempaquetado en uno de los operandos al verificar la igualdad de valores, y simplemente usar los operadores de matemáticas de lo contrario. La razón es la siguiente:

Ya se ha mencionado que == Comparación para Integer es una comparación de identidad, que generalmente no es lo que un progtwigdor quiere, y que el objective es hacer una comparación de valores; aún así, he hecho un poco de ciencia sobre cómo hacer esa comparación de la manera más eficiente, tanto en términos de compacidad, corrección y velocidad del código.

Usé el montón de métodos habituales:

 public boolean method1() { Integer i1 = 7, i2 = 5; return i1.equals( i2 ); } public boolean method2() { Integer i1 = 7, i2 = 5; return i1.intValue() == i2.intValue(); } public boolean method3() { Integer i1 = 7, i2 = 5; return i1.intValue() == i2; } public boolean method4() { Integer i1 = 7, i2 = 5; return i1 == +i2; } public boolean method5() { // obviously not what we want.. Integer i1 = 7, i2 = 5; return i1 == i2; } 

y obtuve este código después de la comstackción y la descomstackción:

 public boolean method1() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); return var1.equals( var2 ); } public boolean method2() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2.intValue() == var1.intValue() ) { return true; } else { return false; } } public boolean method3() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2.intValue() == var1.intValue() ) { return true; } else { return false; } } public boolean method4() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2.intValue() == var1.intValue() ) { return true; } else { return false; } } public boolean method5() { Integer var1 = Integer.valueOf( 7 ); Integer var2 = Integer.valueOf( 5 ); if ( var2 == var1 ) { return true; } else { return false; } } 

Como puede ver fácilmente, el método 1 llama a Integer.equals() (obviamente), los métodos 2-4 dan como resultado exactamente el mismo código , desenvuelve los valores por medio de .intValue() y luego los compara directamente, y el método 5 simplemente desencadena una comparación de identidad, siendo la forma incorrecta de comparar valores.

Dado que (como ya se mencionó por ejemplo JS) equals() incurre en un overhead (tiene que hacer instanceof y un casteo no verificado), los métodos 2-4 funcionarán exactamente con la misma velocidad, notoriamente mejor que el método 1 cuando se usa en bucles ajustados. ya que es poco probable que HotSpot optimice los moldes y la instanceof .

Es bastante similar con otros operadores de comparación (por ejemplo, < / > ): desencadenará unboxing, mientras que intValue() no lo hará, pero esta vez, la operación es altamente optimizable por HS ya que intValue() es solo un método getter (primer candidato a ser optimizado).

En mi opinión, la versión 4 que rara vez se usa es la más concisa: todos los desarrolladores experimentados de C / Java saben que unario plus es en la mayoría de los casos igual a lanzar a int / .intValue() - mientras que puede ser un pequeño momento WTF para algunos (sobre todo aquellos que no usaron el plus unario en su vida), posiblemente muestre el bash de forma más clara y lacónica: muestra que queremos un valor int de uno de los operandos, forzando el otro valor a unbox también. También es indiscutiblemente más similar a la comparación regular i1 == i2 utilizada para los valores int primitivos.

Mi voto va para el estilo i1 == +i2 & i1 > i2 para objetos Integer , tanto por razones de rendimiento como de coherencia. También hace que el código sea portátil para las primitivas sin cambiar nada más que la statement de tipo. El uso de métodos con nombre me parece una introducción de ruido semántico, similar al muy criticado estilo bigInt.add(10).multiply(-3) .

este método compara dos enteros con verificación nula, ver pruebas

 public static boolean compare(Integer int1, Integer int2) { if(int1!=null) { return int1.equals(int2); } else { return int2==null; } //inline version: //return (int1!=null) ? int1.equals(int2) : int2==null; } //results: System.out.println(compare(1,1)); //true System.out.println(compare(0,1)); //false System.out.println(compare(1,0)); //false System.out.println(compare(null,0)); //false System.out.println(compare(0,null)); //false System.out.println(compare(null,null)); //true