2009-03-03 11 views
17

Ich habe eine Klasse, die ich dies vereinfacht:Warum ist mein einfacher Komparator defekt?

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

Ich möchte eine Reihe von dieser Sache sortieren. So habe ich eine einfache copmarator erstellt:

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

ich dann verwenden, um die zwei Argument Form Arrays.sort.

Dies funktioniert gut für meine Testfälle, aber manchmal ist alles falsch mit dem Array in einer seltsamen, aber wiederholbaren Reihenfolge endet. Wie kann das sein?

+0

Geht alles falsch wie? – MarkusQ

+0

Das ist das Puzzle! – erickson

Antwort

20

Integer Überlauf & hellip; oder genauer, Unterlauf.

Stattdessen tun einen expliziten Vergleich:

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

Subtraktion zu verwenden ist in Ordnung, wenn Sie sicher sind, dass die Differenz „Wrap-around“ nicht. Zum Beispiel, wenn die fraglichen Werte nicht negativ sind.

15

Sie können nicht minus verwenden, um den Vergleich zu erstellen. Sie werden überlaufen, wenn die absolute Differenz Integer.MAX_VALUE überschreitet.

Verwenden Sie stattdessen diesen Algorithmus:

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

Ich mag für solche Zwecke in einer Bibliothek, diese Funktion haben.

+0

Für einen Moment wollte ich Sie auf die statische Methode in Integer verweisen. Aber es ist nicht da ... –

+1

@Tom: 'Integer.valueOf (x) .compareTo (y);' ist die prägnanteste Art, die ich mir vorstellen kann. Seltsam, wie Double die statische 'compare()' Methode und die anderen Zahlentypen nicht hat. – Grundlefleck

+2

@Grundlefleck: True! Aber natürlich ist meine Methode viel schneller auszuführen, weil sie keine neue Integer-Instanz erzeugt. –

2

Welche Art von Zahlen werfen Sie dort? Wenn Ihre Zahlen groß genug sind, könnten Sie die MIN/MAX-Werte für Ganzzahlen durchbrechen und in einem Durcheinander enden.

1

Wenn der Wert von a sehr negativ ist und der Wert von b sehr positiv ist, ist die Antwort sehr falsch.

IIRC, Überlauf Int still umschlingt in der JVM

- MarkusQ

5

versuchen

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

Dies muss eine positive Zahl als MAX_VALUE> MIN_VALUE zurückzukehren, sondern druckt -1

+0

+1 für die meisten nicht offensichtlichen "Wrapping" Fail Case. –

4

Beim Vergleich von Java-Primitiven ist es ratsam, sie in ihre Objekt-Gegenstücke zu konvertieren und sich auf ihre compareTo()-Methoden zu stützen.

In diesem Fall können Sie tun:

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

Im Zweifelsfall verwenden Sie eine gut getestete Bibliothek.

+3

Ein bisschen Overhead da (für nicht-kleine Werte). –