Java Integer compareTo () - ¿por qué usar comparación frente a resta?

80

Descubrí que la java.lang.Integerimplementación del compareTométodo se ve de la siguiente manera:

public int compareTo(Integer anotherInteger) {
    int thisVal = this.value;
    int anotherVal = anotherInteger.value;
    return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
}

La pregunta es por qué usar la comparación en lugar de la resta:

return thisVal - anotherVal;
Vladimir
fuente
27
Cuando nos preocupamos tanto por la microoptimización, a menudo terminamos con un código defectuoso.
Kevin Bourrillion
A partir de JDK 7, se puede utilizar en Integer.compare(thisVal, anotherVal)lugar de escribir la expresión ternaria.
Stuart Marks

Respuestas:

96

Esto se debe al desbordamiento de enteros. Cuando thisVales muy grande y anotherVales negativo, restar el último del primero da un resultado que es mayor que el thisValque puede desbordar al rango negativo.

Itay Maman
fuente
Sí, la forma en que lo han hecho aquí es probablemente más eficiente que verificar el desbordamiento y otros
rogerdpack
Utilice Guava ComparisonChain. ¡Es muy útil! google.github.io/guava/releases/22.0/api/docs/com/google/common/…
Andrea Bergonzo
thisValno necesita ser grande. thisValpodría ser incluso cero y anotherValser Integer.MIN_VALUEy ya tiene un desbordamiento. Y tenga en cuenta que, por supuesto, también podría ser al revés, thisValuemuy pequeño y anotherValbastante grande, para tener una distancia que desborde el intrango de valores.
Holger
65

El "truco" de la resta para comparar dos valores numéricos está roto !!!

        int a = -2000000000;
        int b =  2000000000;
        System.out.println(a - b);
        // prints "294967296"

Aquí, a < btodavía a - bes positivo.

NO use este modismo. No funciona.

Además, incluso si funciona , NO proporcionará ninguna mejora significativa en el rendimiento y, de hecho, puede costar la legibilidad.

Ver también

  • Java Puzzlers Puzzle 65: Una extraña saga de tipo sospechoso

    Este rompecabezas tiene varias lecciones. El más específico es: No utilice un comparador basado en restas a menos que esté seguro de que la diferencia entre los valores nunca será mayor que Integer.MAX_VALUE . De manera más general, tenga cuidado con el intdesbordamiento. Otra lección es que debes evitar el código "inteligente". Esfuércese por escribir código claro y correcto, y no lo optimice a menos que sea necesario.

poligenelubricantes
fuente
2
Realmente no está roto en absoluto. Si sabe algo sobre los números que está comparando, probablemente sepa que es seguro compararlos. Incluso sin saberlo, ((long)a - b)debería funcionar. Aunque tienes razón; rara vez es útil.
amara
4
@naiad simplemente haciendo ((long)a - b)no ayuda, ya que tienes que devolver el resultado a int, ya que eso es lo que el comparador tiene que devolver, terminando nuevamente con un desbordamiento. Tendría que hacer algo como Long.signumen el resultado, que es fácil de olvidar, como muestra su comentario. Y puede que ni siquiera sea más eficiente que Integer.compare, lo que la JVM podría manejar intrínsecamente…
Holger
9

Simplemente hablando, el inttipo no es lo suficientemente grande para almacenar la diferencia entre dos intvalores arbitrarios . Por ejemplo, la diferencia entre 1.500 millones y -1.500 millones es de 3.000 millones, pero intno puede contener valores superiores a 2.100 millones.

fredoverflow
fuente
3

Quizás sea para evitar desbordes / subdesbordamientos.

Adamski
fuente
1

Además del desbordamiento, debes tener en cuenta que la versión con sustracción no da los mismos resultados .

  • La primera versión compareTo devuelve uno de los tres valores posibles: -1, 0 o 1.
  • Si reemplaza la última línea con una resta, el resultado puede ser cualquier valor entero.

Si sabe que no habrá desbordamiento, puede usar algo como esto:

public int compareTo(Integer anotherInteger) {
    return sign(this.value - anotherInteger.valuel);
}
PauliL
fuente
12
Tienes razón en que los resultados no son los mismos. ¡Pero no es necesario que lo sean! compareTosolo se requiere para devolver un valor negativo, cero o un valor positivo, según el orden de clasificación de thisy el otro objeto. Ver java.sun.com/j2se/1.5.0/docs/api/java/lang/…
Christian Semrau