Java Integer compareTo () - po co używać porównania a odejmowania?

80

Znalazłem tę java.lang.IntegerimplementacjęcompareTo metody wygląda następująco:

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

Pytanie brzmi, po co używać porównania zamiast odejmowania:

return thisVal - anotherVal;
Vladimir
źródło
27
Kiedy tak szybko martwimy się o mikro-optymalizację, często kończy się to błędnym kodem.
Kevin Bourrillion
Od JDK 7 Integer.compare(thisVal, anotherVal)zamiast wypisywać wyrażenie trójskładnikowe.
Stuart Marks

Odpowiedzi:

96

Wynika to z przepełnienia liczb całkowitych. Gdy thisValjest bardzo duże i anotherValjest ujemne, odjęcie tego drugiego od pierwszego daje wynik, który jest większy niż ten, thisValktóry może przelać się do zakresu ujemnego.

Itay Maman
źródło
Tak, sposób, w jaki to zrobili, jest prawdopodobnie bardziej wydajny niż sprawdzanie przepełnienia i innych
rogerdpack
Użyj Guava CompareChain. To jest bardzo przydatne! google.github.io/guava/releases/22.0/api/docs/com/google/common/…
Andrea
thisValnie musi być duży. thisValmoże być nawet zero i anotherValbyć, Integer.MIN_VALUEa już masz przepełnienie. I pamiętaj, że oczywiście może być odwrotnie, thisValuebardzo mały i anotherValraczej duży, aby odległość przekraczała intzakres wartości.
Holger
65

„Sztuczka” odejmowania polegająca na porównaniu dwóch wartości liczbowych jest zepsuta !!!

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

Tutaj a < bjednak a - bjest pozytywne.

NIE używaj tego idiomu. To nie działa.

Co więcej, nawet jeśli zadziała , NIE zadziała zapewni żadnej znaczącej poprawy wydajności i może w rzeczywistości kosztować czytelność.

Zobacz też

  • Java Puzzlers Puzzle 65: A Strange Saga of Suspicious Rodzaj

    Ta łamigłówka ma kilka lekcji. Najbardziej szczegółowe to: Nie używaj komparatora opartego na odejmowaniu, chyba że masz pewność, że różnica między wartościami nigdy nie będzie większa niż Integer.MAX_VALUE . Mówiąc bardziej ogólnie, uważaj na intprzepełnienie. Inną lekcją jest to, że należy unikać „sprytnego” kodu. Staraj się pisać jasny, poprawny kod i nie optymalizuj go, chyba że okaże się to konieczne.

smary wielogenowe
źródło
2
To wcale nie jest zepsute. Jeśli wiesz coś o liczbach, które porównujesz, prawdopodobnie wiesz, że można je bezpiecznie porównać. Nawet nie wiedząc, po prostu ((long)a - b)powinno działać. Chociaż masz rację; bardzo rzadko jest przydatne.
amara
4
@naiad po prostu ((long)a - b)nie pomaga, ponieważ musisz rzutować wynik z powrotem int, ponieważ to właśnie musi zwrócić komparator, kończąc ponownie z przepełnieniem. Musiałbyś zrobić coś podobnego Long.signumdo wyniku, o którym łatwo zapomnieć, jak pokazuje Twój komentarz. I może nie być nawet bardziej wydajne niż to Integer.compare, z czym JVM mogłaby sobie poradzić…
Holger
9

Mówiąc najprościej, inttyp nie jest wystarczająco duży, aby zapisać różnicę między dwiema dowolnymi intwartościami. Na przykład różnica między 1,5 miliarda a -1,5 miliarda wynosi 3,0 miliarda, ale intnie może zawierać wartości większych niż 2,1 miliarda.

fredoverflow
źródło
3

Być może ma to na celu uniknięcie przepełnienia / niedomiaru.

Adamskiego
źródło
1

Oprócz przepełnienia należy zauważyć, że wersja z odejmowaniem nie daje takich samych wyników .

  • Pierwsza wersja compareTo zwraca jedną z trzech możliwych wartości: -1, 0 lub 1.
  • Jeśli zamieniasz ostatni wiersz na odejmowanie, wynikiem może być dowolna liczba całkowita.

Jeśli wiesz, że nie będzie przepełnienia, możesz użyć czegoś takiego:

public int compareTo(Integer anotherInteger) {
    return sign(this.value - anotherInteger.valuel);
}
PauliL
źródło
12
Masz rację, że wyniki nie są takie same. Ale nie jest to wymagane! compareTojest wymagane tylko do zwrócenia wartości ujemnej, zerowej lub dodatniej, w zależności od kolejności sortowania thisi innego obiektu. Zobacz java.sun.com/j2se/1.5.0/docs/api/java/lang/…
Christian Semrau