Czy ktoś może mi wyjaśnić w prosty sposób, dlaczego ten kod generuje wyjątek „Metoda porównawcza narusza ogólną umowę!” I jak to naprawić?
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
java
comparator
n00bster
źródło
źródło
s1.getParent().equals(s2)
zamiasts1.getParent() == s2
.s1
jest rodzicems2
is2
nie jest rodzicems1
. WięccompareParents(s1, s2)
jest0
, alecompareParents(s2, s1)
jest1
. To nie ma sensu. (Ponadto nie jest przechodni, jak wspomniano poniżej aix.)Odpowiedzi:
Twój komparator nie jest przechodni.
Pozwól
A
być rodzicemB
iB
być rodzicemC
. OdtądA > B
iB > C
wtedy musi tak byćA > C
. Jednak jeśli twój komparator zostanie wywołanyA
iC
zwróci zero, co oznaczaA == C
. Narusza to umowę i dlatego zgłasza wyjątek.Biblioteką jest raczej miło to wykryć i powiadomić, niż zachowywać się niepoprawnie.
Jednym ze sposobów spełnienia wymogu przechodniości
compareParents()
jest przejściegetParent()
łańcucha zamiast patrzenia tylko na bezpośredniego przodka.źródło
java.util.Arrays.sort
stackoverflow.com/questions/7849539/Tylko dlatego, że właśnie to dostałem, kiedy przejrzałem ten błąd, mój problem polegał na tym, że go miałem
value >= other.value
powinien (oczywiście) rzeczywiście byćvalue > other.value
tak, że rzeczywiście można wrócić 0 z jednakowych przedmiotów.źródło
value
jest NaN (jeślivalue
jest adouble
lubfloat
), to również by się nie udało.Naruszenie umowy często oznacza, że komparator nie zapewnia poprawnej lub spójnej wartości przy porównywaniu obiektów. Na przykład możesz chcieć wykonać porównanie ciągów i zmusić puste ciągi do sortowania do końca za pomocą:
Ale pomija się przypadek, w którym ZARÓWNO jeden i dwa są puste - w takim przypadku zwracana jest niewłaściwa wartość (1 zamiast 0, aby pokazać dopasowanie), a komparator zgłasza to jako naruszenie. Powinien być napisany jako:
źródło
Nawet jeśli twoje porównanie ma teoretycznie charakter przechodni, czasami subtelne błędy psują rzeczy ... na przykład błąd arytmetyczny zmiennoprzecinkowy. Zdarzyło mi się. to był mój kod:
Właściwość przechodnia wyraźnie zachowuje się, ale z jakiegoś powodu otrzymywałem IllegalArgumentException. I okazuje się, że z powodu drobnych błędów w arytmetyki zmiennoprzecinkowej błędy zaokrąglania powodowały, że właściwość przechodnia pękała tam, gdzie nie powinna! Przepisałem więc kod, aby uwzględnić naprawdę małe różnice 0 i zadziałało:
źródło
W naszym przypadku otrzymywaliśmy ten błąd, ponieważ przypadkowo odwróciliśmy kolejność porównywania s1 i s2. Więc uważaj na to. Było to oczywiście o wiele bardziej skomplikowane niż poniższe, ale to jest ilustracja:
źródło
Java nie sprawdza spójności w ścisłym znaczeniu, tylko powiadamia cię, jeśli napotka poważne kłopoty. Również nie daje dużo informacji o błędzie.
Zaskoczyło mnie to, co dzieje się w moim sortowniku i zachowałem ścisłą spójność Checker, może to ci pomoże:
źródło
Compare
,Convert
(i potencjalnie inne) nie są zdefiniowane. Zaktualizuj fragment kodu samodzielnym przykładem.checkConsi(s)tency
i usunąć wszystkie zbędne@param
deklaracje, aby kod był bardziej czytelny.W moim przypadku robiłem coś takiego:
Zapomniałem sprawdzić, kiedy zarówno a.someField, jak i b.someField są zerowe.
źródło
Widziałem, jak to się dzieje w kodzie, w którym przeprowadzano często powtarzające się sprawdzanie wartości pustych:
źródło
Jeśli
compareParents(s1, s2) == -1
takcompareParents(s2, s1) == 1
jest oczekiwane. W twoim kodzie nie zawsze jest to prawda.Szczególnie jeśli
s1.getParent() == s2 && s2.getParent() == s1
. To tylko jeden z możliwych problemów.źródło
Edycja konfiguracji VM działała dla mnie.
źródło
-
do początku proponowanego rozwiązania. Być może zamiast tego zamierzałeś stworzyć jedną punktowaną listę punktowaną.Nie można porównywać danych obiektu w następujący sposób:
s1.getParent() == s2
- spowoduje to porównanie odniesień do obiektu. Powinieneś przesłonićequals function
klasę Foo, a następnie porównać je w ten sposóbs1.getParent().equals(s2)
źródło