Java Integer compareTo () - warum Vergleich vs. Subtraktion verwenden?

80

Ich habe festgestellt, dass die java.lang.IntegerImplementierung der compareToMethode wie folgt aussieht:

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

Die Frage ist, warum Vergleich statt Subtraktion verwendet wird:

return thisVal - anotherVal;
Vladimir
quelle
27
Wenn wir uns so schnell um die Mikrooptimierung kümmern, erhalten wir häufig fehlerhaften Code.
Kevin Bourrillion
Ab JDK 7 kann man Integer.compare(thisVal, anotherVal)den ternären Ausdruck verwenden, anstatt ihn zu schreiben.
Stuart Marks

Antworten:

96

Dies ist auf einen ganzzahligen Überlauf zurückzuführen. Wenn thisVales sehr groß und anotherValnegativ ist, ergibt das Subtrahieren des letzteren vom ersteren ein Ergebnis, das größer ist als thisValdas, das in den negativen Bereich überlaufen kann.

Itay Maman
quelle
Ja so , wie sie es hier getan hat , ist wahrscheinlich effizienter als Kontrolle für den Schwall et al
rogerdpack
Verwenden Sie Guava ComparisonChain. Es ist sehr praktisch! google.github.io/guava/releases/22.0/api/docs/com/google/common/…
Andrea Bergonzo
thisValmuss nicht groß sein. thisValkönnte sogar Null sein und anotherValsein Integer.MIN_VALUEund Sie haben bereits einen Überlauf. Und denken Sie daran, dass es natürlich auch umgekehrt sein könnte, thisValuesehr klein und anotherValziemlich groß, einen Abstand zu haben, der über den intWertebereich hinausgeht.
Holger
65

Der Subtraktions- "Trick" zum Vergleichen zweier numerischer Werte ist gebrochen !!!

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

Hier ist a < bdoch a - bnoch positiv.

Verwenden Sie diese Redewendung NICHT. Es funktioniert nicht.

Außerdem , selbst wenn es funktioniert , wird es nicht eine wesentliche Verbesserung der Leistung, und kann in der Tat Kosten Lesbarkeit.

Siehe auch

  • Java Puzzlers Puzzle 65: Eine seltsame Saga verdächtiger Art

    Dieses Puzzle hat mehrere Lektionen. Das spezifischste ist: Verwenden Sie keinen subtraktionsbasierten Komparator, es sei denn, Sie sind sicher, dass der Unterschied zwischen den Werten niemals größer als sein wird Integer.MAX_VALUE . Achten Sie generell auf intÜberlauf. Eine weitere Lektion ist, dass Sie "cleveren" Code vermeiden sollten. Bemühen Sie sich, klaren, korrekten Code zu schreiben, und optimieren Sie ihn nur, wenn er sich als notwendig erweist.

Polygenschmierstoffe
quelle
2
Es ist überhaupt nicht wirklich kaputt. Wenn Sie etwas über die Zahlen wissen, die Sie vergleichen, wissen Sie wahrscheinlich, dass sie sicher zu vergleichen sind. Auch wenn ((long)a - b)ich es nicht weiß, sollte es einfach funktionieren. Obwohl du recht hast; es ist sehr selten nützlich.
Amara
4
@naiad nur zu tun ((long)a - b)hilft nicht, da Sie das Ergebnis zurücksetzen müssen int, da dies der Vergleicher zurückgeben muss, was wiederum zu einem Überlauf führt. Sie müssten so etwas wie Long.signumdas Ergebnis tun , was leicht zu vergessen ist, wie Ihr Kommentar zeigt. Und es ist vielleicht nicht einmal effizienter als das Integer.compare, was die JVM an sich handhaben könnte ...
Holger
9

Einfach ausgedrückt ist der intTyp nicht groß genug, um die Differenz zwischen zwei beliebigen intWerten zu speichern . Beispielsweise beträgt die Differenz zwischen 1,5 und -1,5 Milliarden 3,0 Milliarden, intkann jedoch keine Werte von mehr als 2,1 Milliarden enthalten.

Fredoverflow
quelle
3

Vielleicht, um Überlauf / Unterlauf zu vermeiden.

Adamski
quelle
1

Zusätzlich zum Überlauf sollten Sie beachten, dass die Version mit Subtraktion nicht die gleichen Ergebnisse liefert .

  • Die erste compareTo-Version gibt einen von drei möglichen Werten zurück: -1, 0 oder 1.
  • Wenn Sie die letzte Zeile durch eine Subtraktion ersetzen, kann das Ergebnis ein beliebiger ganzzahliger Wert sein.

Wenn Sie wissen, dass es keinen Überlauf gibt, können Sie Folgendes verwenden:

public int compareTo(Integer anotherInteger) {
    return sign(this.value - anotherInteger.valuel);
}
PauliL
quelle
12
Sie haben Recht, dass die Ergebnisse nicht gleich sind. Aber sie müssen nicht sein! compareToist nur erforderlich, um einen negativen Wert, Null oder einen positiven Wert zurückzugeben, abhängig von der Sortierreihenfolge thisund dem anderen Objekt. Siehe java.sun.com/j2se/1.5.0/docs/api/java/lang/…
Christian Semrau