2009-03-03 12 views
17

tengo una clase, que he simplificado a esto:¿Por qué está roto mi simple comparador?

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 un arreglo 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 Arrays.sort.

Esto funciona bien para mis casos de prueba, pero a veces todo va mal con la matriz terminando en un orden extraño pero repetible. ¿Cómo puede ser esto?

+0

¿Va todo mal? ¿Cómo? – MarkusQ

+0

¡Ese es el rompecabezas! – erickson

Respuesta

20

Desbordamiento de enteros & hellip; o más precisamente, underflow.

En su lugar, hacer 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); 
    } 
}; 

Usando la resta está bien si está seguro de que la diferencia no va a "envolver". Por ejemplo, cuando los valores en cuestión están limitados a ser no negativos.

15

No puede usar menos para crear la comparación. Se desbordará cuando la diferencia absoluta supere Integer.MAX_VALUE.

su lugar, utilice este algoritmo:

int compareInts(int x, int y) { 
    if (x < y) return -1; 
    if (x > y) return 1; 
    return 0; 
} 

Me gustaría tener esta función en una biblioteca para tales fines.

+0

Por un momento, iba a señalarte el método estático en Integer. Pero no está allí ... –

+1

@Tom: 'Integer.valueOf (x) .compareTo (y);' es la manera más breve que se me ocurre. Es extraño cómo Double tiene el método estático 'compare()' y los otros tipos de números no. – Grundlefleck

+2

@Grundlefleck: ¡Es cierto! Pero, por supuesto, mi método es mucho más rápido de ejecutar porque no crea una nueva instancia 'Integer'. –

2

¿Qué tipo de números arrojas ahí? Si sus números son lo suficientemente grandes, podría envolver los valores MIN/MAX para enteros y terminar en un lío.

1

Si el valor de a es muy negativo y el valor de b es muy positivo, su respuesta será muy incorrecta.

IIRC, Int desbordamiento en silencio envuelve alrededor de la JVM

- MarkusQ

5

tratar

System.out.println(Integer.MAX_Value - Integer.MIN_VALUE); 

Esto tiene que devolver un número positivo como MAX_VALUE> MIN_VALUE sino que imprime -1

+0

+1 para el caso de fallo de "envoltura" más obvio. –

4

Al comparar las primitivas de Java, es aconsejable convertirlas a sus contrapartidas de objetos y confiar en sus métodos compareTo().

En este caso se puede hacer:

return Integer.valueOf(a.getValue()).compareTo(b.getValue()) 

En caso de duda, utilice una biblioteca bien probado.

+3

Bit de una sobrecarga allí (para valores no pequeños). –

Cuestiones relacionadas