¿Por qué está roto mi simple comparador?
Pregunta
Tengo una clase, que he simplificado a esto:
final class Thing {
private final int value;
public Thing(int value) {
this.value = value;
}
public int getValue() {
return value;
}
@Override public String toString() {
return Integer.toString(value);
}
}
Quiero ordenar una matriz de esta cosa. Así que he creado un copmarator simple:
private static final Comparator<Thing> reverse = new Comparator<Thing>() {
public int compare(Thing a, Thing b) {
return a.getValue() - b.getValue();
}
};
Luego uso la forma de dos argumentos de Arrays.sort
.
Esto funciona bien para mis casos de prueba, pero a veces no funciona bien con el arreglo que termina en un orden extraño pero repetible. ¿Cómo puede ser esto?
Solución
Desbordamiento de enteros ... o más precisamente, subdesbordamiento.
En su lugar, haz una comparación explícita:
private static final Comparator<Thing> reverse = new Comparator<Thing>() {
public int compare(Thing a, Thing b) {
int av = a.getValue(), bv = b.getValue();
return (av == bv) ? 0 : ((av < bv) ? -1 : +1);
}
};
El uso de la resta está bien si está seguro de que la diferencia no se ajustará a " ;. Por ejemplo, cuando los valores en cuestión se limitan a no ser negativos.
Otros consejos
No puedes usar menos para crear la comparación. Se desbordará cuando la diferencia absoluta supere Integer.MAX_VALUE
.
En su lugar, usa este algoritmo:
int compareInts( int x, int y ) {
if ( x < y ) return -1;
if ( x > y ) return 1;
return 0;
}
Me gusta tener esta función en una biblioteca para tales fines.
prueba
System.out.println(Integer.MAX_Value - Integer.MIN_VALUE);
Esto debe devolver un número positivo como MAX_VALUE > MIN_VALUE pero en su lugar imprime -1
Al comparar primitivos de Java, es recomendable convertirlos a sus contrapartes de objetos y confiar en sus métodos compareTo ()
.
En este caso puedes hacer:
return Integer.valueOf(a.getValue()).compareTo(b.getValue())
En caso de duda, utilice una biblioteca bien probada.
¿Qué tipo de números arrojas allí? Si sus números son lo suficientemente grandes, podría ajustar los valores MIN / MAX para los enteros y terminar en un desastre.
Si el valor de a es muy negativo y el valor de b es muy positivo, su respuesta será muy incorrecta.
IIRC, Int overflow se envuelve silenciosamente en la JVM
- MarkusQ