2009-03-03 6 views
17

Ho una classe, che ho semplificato a questo:Perché il mio semplice comparatore è rotto?

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); 
    } 
} 

Voglio ordinare un array di questa cosa. Così ho creato un semplice copmarator:

private static final Comparator<Thing> reverse = new Comparator<Thing>() { 
    public int compare(Thing a, Thing b) { 
     return a.getValue() - b.getValue(); 
    } 
}; 

Ho quindi utilizzare la forma a due argomenti di Arrays.sort.

Questo funziona bene per i miei casi di test, ma a volte va tutto storto con la matrice che finisce in un ordine strano ma ripetibile. Come può essere?

+0

Va tutto bene, come? – MarkusQ

+0

Questo è il puzzle! – erickson

risposta

20

Intero overflow & hellip; o più precisamente, underflow.

Invece, fare un confronto esplicito:

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); 
    } 
}; 

Utilizzando sottrazione va bene se si è certi che la differenza non sarà "wrap around". Ad esempio, quando i valori in questione sono vincolati per essere non negativi.

15

Non è possibile utilizzare il segno meno per creare il confronto. Trabocchi quando la differenza assoluta supera Integer.MAX_VALUE.

Invece, utilizzare questo algoritmo:

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

mi piace avere questa funzione in una libreria per tali scopi.

+0

Per un momento stavo per indicarti il ​​metodo statico in Integer. Ma non è lì ... –

+1

@Tom: 'Integer.valueOf (x) .compareTo (y);' è il modo più succinto che riesca a pensare. Strano come Double abbia il metodo statico 'compare()' e gli altri tipi di numeri no. – Grundlefleck

+2

@Grundlefleck: vero! Ma ovviamente il mio metodo è molto più veloce da eseguire perché non crea una nuova istanza 'Integer'. –

2

Che tipo di numeri ci passi dentro? Se i tuoi numeri sono abbastanza grandi, puoi avvolgere i valori MIN/MAX per i numeri interi e finire in disordine.

1

Se il valore di a è molto negativo e il valore di b è molto positivo, la risposta sarà molto sbagliata.

IIRC, Int troppo pieno avvolge in silenzio attorno nella JVM

- MarkusQ

5

provare

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

Questo deve restituire un numero positivo come MAX_VALUE> MIN_VALUE ma invece stampe -1

+0

+1 per il caso di errore "involucro" più ovvio. –

4

Quando si confrontano i primitivi Java, è consigliabile convertirli nelle loro controparti oggetto e fare affidamento sui loro metodi compareTo().

In questo caso si può fare:

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

In caso di dubbio, utilizzare una libreria ben collaudato.

+3

Bit di un overhead lì (per valori non piccoli). –