¿Cómo comparar correctamente dos enteros en Java?

217

Sé que si compara un entero primitivo en caja 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 compara dos recuadros Integersy desea comparar igualdad o menor que / mayor que?

Integer a = 4;
Integer b = 5;

if (a == b)

¿El código anterior resultará en una comprobación para ver si son el mismo objeto, o se desempaquetará automáticamente en ese caso?

Qué pasa:

Integer a = 4;
Integer b = 5;

if (a < b)

?

shmosel
fuente
16
Bueno, ¿qué pasó cuando lo intentaste? ¿Qué observaste?
Bart Kiers el
31
@Bart Kiers: un experimento explícito solo podría refutar, no probar que se produce el desempaquetado. Si el uso en ==lugar de equalsarroja el resultado correcto, puede deberse a que los números en caja están siendo internados o reutilizados (como una optimización del compilador, presumiblemente). La razón para hacer esta pregunta es descubrir qué está sucediendo internamente, no qué parece estar sucediendo. (Al menos, por eso estoy aquí.)
Jim Pivarski
¿Qué le pasó a tu cuenta?

Respuestas:

303

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

Integer x = ...;
Integer y = ...;

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

esta comprobará si xy yreferirse al mismo objeto en lugar de la igualdad de objetos.

Entonces

Integer x = new Integer(10);
Integer y = new Integer(10);

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

Está garantizado para imprimir false. La internación de valores "pequeños" autoboxed puede conducir a resultados difíciles:

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 realmente iguales.

Si el valor p encuadrado es un literal entero de tipo int entre -128 y 127 inclusive (§3.10.1), o el literal booleano verdadero o falso (§3.10.3), o un carácter literal entre '\ u0000' y '\ u007f' inclusive (§3.10.4), luego deje que ayb sean los resultados de dos conversiones de p. Siempre es el caso que a == b.

Personalmente usaría:

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

o

if (x.equals(y))

Como usted dice, para cualquier comparación entre un tipo de contenedor ( Integer, Longetc.) y un tipo numérico ( int, longetc.) el valor del tipo de contenedor es caja y la prueba se aplica a los valores primitivos involucrados.

Esto ocurre como parte de la promoción numérica binaria ( JLS sección 5.6.2 ). 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 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érico debe ser un tipo que sea convertible (§5.1.8) a un tipo numérico primitivo, o se producirá un error en tiempo de compilació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 long, se realiza una comparación de enteros con signo; si este tipo promocionado es flotante o doble, se realiza una comparación de punto flotante.

Tenga en cuenta que nada de esto se considera parte de la situación en la que ninguno de los dos tipos es numérico.

Jon Skeet
fuente
2
¿Hay alguna razón por la que uno quisiera escribir en x.compareTo(y) < 0lugar de x < y?
Max Nanasy
1
@MaxNanasy: No es que pueda pensar de inmediato.
Jon Skeet
2
A partir de Java 1.6.27+, existe una sobrecarga igual en la clase Integer, por lo que debería ser tan eficiente como llamar a .intValue (). Compara los valores como primitivo int.
otterslide
Como dijo @otterslide, esto ya no es necesario en Java 8. La comparación de Integer con Integer es por valor por defecto.
Axel Prieto
1
@Axel: la adición de una sobrecarga no cambiaría el comportamiento del operador ==, ¿verdad? No estoy en condiciones de probar en este momento, pero me sorprendería mucho si eso hubiera cambiado.
Jon Skeet
44

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

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 aún se verificará. En este caso, cuando inicializa los objetos desde una primitiva en caja, se utiliza el mismo objeto (tanto para a como para b). Esta es una optimización correcta ya que las clases de caja primitivas son inmutables.

Adam Lewis
fuente
Supuse que se estaba probando la igualdad de objetos. Tuve algunos resultados extraños. ¿Debo reemplazarlo con .equals ()? Además, ¿crees que debería dejar las desigualdades como están o hacerlo de otra manera también?
Hay algunos casos extremos no obvios con autoboxing. Tengo mi IDE (Eclipse) configurado para colorear cualquier cosa que no esté en caja en rojo, esto me ha salvado de errores en algunas ocasiones. Si está comparando dos enteros, use .equals, si desea aclarar sus desigualdades, escriba explícitamente el elenco: if ((int) c <(int) d) ...; También puede hacer: c.compareTo (d) <0 // === c <d
Adam Lewis el
12
Y si cambia los literales numéricos a 200, se imprimirán ambas pruebas false.
Daniel Earwicker
2
... en la mayoría de las implementaciones de JVM, eso es. De acuerdo con las especificaciones del lenguaje, el resultado puede variar entre implementaciones.
Daniel Earwicker
44
Creo que es más claro llamar a esto "igualdad de referencia", de esa manera es obvio lo que quieres decir. Normalmente entendería "igualdad de objeto" en el sentido de "el resultado de equalsser llamado".
Jon Skeet el
28

Desde Java 1.7 puede usar Objects.equals :

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

Devuelve verdadero si los argumentos son iguales entre sí y falso en caso 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 igual del primer argumento.

Tal como
fuente
Esto maneja los valores nulos, por lo que es simple. ¡Gracias!
Darren Parker
10

== 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 ay 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
Cory Kendall
fuente
1
tl; dr -1 para agitar las manos; stackoverflow.com/questions/15052216/… stackoverflow.com/questions/20897020/… stackoverflow.com/questions/3131136/integers-caching-in-java, etc. explica en detalle el asunto que mencionaste; es mejor leer los documentos (o la fuente lib) que crear pseudo-pruebas con el riesgo de una alta localidad de los resultados, no solo se ha olvidado por completo del límite inferior del caché (es decir, -128 por defecto), no solo tienes off-by-one (el máximo es 127, no 128),
pero no tiene ninguna garantía de recibir el mismo resultado en ninguna máquina, ya que puede aumentar fácilmente el tamaño de la memoria caché, YMMV. Además, la pregunta de OP era cómo comparar correctamente dos enteros : no lo ha respondido en absoluto .
Respeto tu opinión y percepción aquí. Creo que solo tenemos enfoques fundamentalmente diferentes para CS.
Cory Kendall
1
no se trata de opinión ni percepción , se trata de los hechos , que sinceramente te perdiste. Hacer una pseudo-prueba que no demuestre nada, sin datos de respaldo (documentos, fuente, etc.) y sin responder las preguntas de OP no merece ser llamado ni Q&A ni CS. En cuanto a "enfoque diferente" - CS es, por definición, una ciencia ; lo que hiciste ciencia no es ; es una trivia engañosa (o sería un comentario intrigante , si se indica correctamente): si desea que sea ciencia , corrija los defectos fundamentales en su respuesta o desacredítelos de manera sensata , ya que así es comotrabajos.
Claro, intentaré solucionar los defectos. No me olvidé del límite inferior, no sentí que fuera interesante y decidí no incluirlo. No creo que tenga un error por un error, dije la forma en que Java (que aclaré en mi máquina, en mi situación) dejó de optimizar esto, que está en 128. Si hubiera declarado el valor máximo, lo hizo esto para, que tienes razón, la respuesta habría sido 127.
Cory Kendall
8

tl; dr, mi opinión es usar un unario +para activar el desempaquetado en uno de los operandos al verificar la igualdad de valores, y simplemente usar los operadores matemáticos de lo contrario. La justificación sigue:

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

Usé los 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 compilación y descompilació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 Integer.equals()(obviamente), los métodos 2-4 dan como resultado exactamente el mismo código , desenvolviendo los valores mediante.intValue() y luego comparándolos 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 una sobrecarga (tiene que hacer instanceofy un lanzamiento no verificado), los métodos 2-4 funcionarán con exactamente la misma velocidad, notoriamente mejor que el método 1 cuando se usa en bucles estrechos, ya que HotSpot no es probable optimizar los moldes & instanceof.

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

En mi opinión, la versión 4 que rara vez se utiliza es la forma más concisa: cada desarrollador experimentado de C / Java sabe que unary plus es en la mayoría de los casos igual a emitir a int/ .intValue(), mientras que puede ser un pequeño momento WTF para algunos (principalmente aquellos que no lo hicieron) no use unary plus en su vida), posiblemente muestre la intención de la manera más clara y clara: muestra que queremos un intvalor de uno de los operandos, lo que obliga al otro valor a desempaquetar también. También es indiscutiblemente más similar a la i1 == i2comparación regular utilizada para intvalores primitivos .

Mi voto va para i1 == +i2y i1 > i2estilo para Integerlos objetos, tanto por razones de rendimiento y consistencia. También hace que el código sea portátil para las primitivas sin cambiar nada más que la declaración de tipo. Usar métodos con nombre me parece introducir ruido semántico, similar al bigInt.add(10).multiply(-3)estilo muy criticado .


fuente
¿Puedes explicar qué significa el + en el método 4? Intenté buscarlo en Google pero solo obtuve los usos normales de ese símbolo (además, concatenación).
Alex Li
1
@AlexLi significa exactamente lo que escribí - unary +(unary plus), ver por ejemplo stackoverflow.com/questions/2624410/…
8

Vocación

if (a == b)

Funcionará la mayor parte del tiempo, pero no está garantizado 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 denominen 'a' y 'b' es llamar:

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

También puede usar esta forma, que es un poco más rápida.

   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 usando el primer método y 46 segundos usando el segundo método. Tendría que comparar miles de millones de valores para ver cualquier diferencia.

Tenga en cuenta que 'a' puede ser nulo ya que es un objeto. Comparar de esta manera no causará una excepción de puntero nulo.

Para comparar mayor y menor que, 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");
}
tobogán de nutria
fuente
1
if (a==b)funciona solo para valores pequeños y no funcionará la mayoría de las veces.
Tony
Funciona hasta 127, ya que es el caché de enteros predeterminado de Java, lo que garantiza que todos los números hasta 127 tengan el mismo valor de referencia. Puede configurar el caché para que sea superior a 127 si lo desea, pero simplemente no use == para estar seguro.
otterslide
3

Siempre debemos utilizar el método equals () para comparar dos enteros. Es la práctica recomendada.

Si comparamos dos enteros usando == eso funcionaría para cierto rango de valores enteros (Entero de -128 a 127) debido a la optimización interna de JVM.

Por favor vea ejemplos:

Caso 1:

Entero a = 100; Entero b = 100;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

En el caso anterior, JVM usa el valor de a y b del grupo en caché y devuelve la misma instancia de objeto (por lo tanto, la dirección de memoria) del objeto entero y obtenemos que ambos son iguales. Su optimización JVM lo hace para ciertos valores de rango.

Caso 2: en este caso, ayb no son iguales porque no viene con el rango de -128 a 127.

Entero a = 220; Entero b = 220;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

Forma apropiada:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true

Espero que esto ayude.

Siyaram Malav
fuente
1

En mi caso, tuve que comparar dos Integers para la igualdad donde ambos podrían estar null. Busqué un tema similar, no encontré nada elegante para esto. Se le ocurrió una sencilla utilidad de funciones.

public static boolean integersEqual(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return true;
    }
    if (i1 == null && i2 != null) {
        return false;
    }
    if (i1 != null && i2 == null) {
        return false;
    }
    return i1.intValue() == i2.intValue();
}

//considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return 0;
    }
    if (i1 == null && i2 != null) {
        return -1;
    }
    return i1.compareTo(i2);
}
Martillo neumático
fuente
-1

Debido a que el método de comparación debe hacerse en base al tipo int (x == y) o la clase Integer (x.equals (y)) con el operador correcto

public class Example {

    public static void main(String[] args) {
     int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<arr.length-1; j++)
            if((arr[j-1]!=arr[j]) && (arr[j]!=arr[j+1])) 
                System.out.println("int>"+arr[j]);


    Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<I_arr.length-1; j++)
            if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1]))) 
                System.out.println("Interger>"+I_arr[j]);
    }
}
Chronoslog
fuente
-2

este método compara dos números 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
Alex Torson
fuente
44
Para esto, creo que sería mejor usar el Objects.equals(x,y)método en lugar de usar el tuyo.
ryvantage