Kann mir jemand in einfachen Worten erklären, warum dieser Code eine Ausnahme auslöst: "Die Vergleichsmethode verstößt gegen ihren allgemeinen Vertrag!" Und wie behebe ich sie?
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
java
comparator
n00bster
quelle
quelle
s1.getParent().equals(s2)
stattdessen tun solltens1.getParent() == s2
.s1
ist das Elternteil vons2
unds2
ist nicht das Elternteil vons1
. DanncompareParents(s1, s2)
ist0
, abercompareParents(s2, s1)
ist1
. Das macht keinen Sinn. (Außerdem ist es nicht transitiv, wie unten erwähnt.)Antworten:
Ihr Komparator ist nicht transitiv.
Sei
A
der Elternteil vonB
undB
sei der Elternteil vonC
. SeitA > B
undB > C
dann muss es so seinA > C
. Wenn Ihr Komparator jedoch aufA
und aufgerufen wirdC
, gibt er Null zurück, was bedeutetA == C
. Dies verstößt gegen den Vertrag und löst daher die Ausnahme aus.Es ist ziemlich nett von der Bibliothek, dies zu erkennen und Sie wissen zu lassen, anstatt sich unberechenbar zu verhalten.
Eine Möglichkeit, die Transitivitätsanforderung zu erfüllen,
compareParents()
besteht darin, diegetParent()
Kette zu durchlaufen , anstatt nur den unmittelbaren Vorfahren zu betrachten.quelle
java.util.Arrays.sort
stackoverflow.com/questions/7849539/…Nur weil ich das bekommen habe, als ich diesen Fehler gegoogelt habe, war mein Problem, dass ich es hatte
Das
value >= other.value
sollte (offensichtlich) tatsächlichvalue > other.value
so sein, dass Sie tatsächlich 0 mit gleichen Objekten zurückgeben können.quelle
value
ein NaN ist (wennvalue
es eindouble
oder istfloat
), es auch scheitern würde.Die Vertragsverletzung führt häufig dazu, dass der Komparator beim Vergleich von Objekten nicht den richtigen oder konsistenten Wert liefert. Beispielsweise möchten Sie möglicherweise einen Zeichenfolgenvergleich durchführen und leere Zeichenfolgen zwingen, bis zum Ende zu sortieren mit:
Dies übersieht jedoch den Fall, in dem sowohl eins als auch zwei leer sind - und in diesem Fall wird der falsche Wert zurückgegeben (1 anstelle von 0, um eine Übereinstimmung anzuzeigen), und der Komparator meldet dies als Verstoß. Es sollte geschrieben worden sein als:
quelle
Selbst wenn Ihr compareTo theoretisch die Transitivität enthält, bringen manchmal subtile Fehler die Dinge durcheinander ... wie z. B. Gleitkomma-Rechenfehler. Es ist mir passiert. Das war mein Code:
Die transitive Eigenschaft ist eindeutig gültig, aber aus irgendeinem Grund habe ich die IllegalArgumentException erhalten. Und es stellt sich heraus, dass aufgrund winziger Fehler in der Gleitkomma-Arithmetik die Rundungsfehler dazu führten, dass die transitive Eigenschaft dort brach, wo sie nicht sollte! Also habe ich den Code umgeschrieben, um wirklich winzige Unterschiede 0 zu berücksichtigen, und es hat funktioniert:
quelle
In unserem Fall wurde dieser Fehler angezeigt, weil wir versehentlich die Vergleichsreihenfolge von s1 und s2 umgedreht hatten. Also pass auf. Es war offensichtlich viel komplizierter als das Folgende, aber dies ist eine Illustration:
quelle
Java überprüft die Konsistenz nicht im engeren Sinne, sondern benachrichtigt Sie nur, wenn ernsthafte Probleme auftreten. Es gibt Ihnen auch nicht viele Informationen über den Fehler.
Ich war verwirrt über das, was in meinem Sortierer passiert, und habe einen strengen Konsistenzchecker erstellt. Vielleicht hilft Ihnen das:
quelle
Compare
,Convert
(und möglicherweise andere) sind nicht definiert. Bitte aktualisieren Sie das Code-Sniplet mit einem in sich geschlossenen Beispiel.checkConsi(s)tency
und alle redundanten@param
Deklarationen entfernen , um den Code besser lesbar zu machen.In meinem Fall habe ich so etwas gemacht:
Was ich vergessen habe zu überprüfen war, wenn sowohl a.someField als auch b.someField null sind.
quelle
Ich habe dies in einem Code gesehen, in dem die häufig wiederkehrende Überprüfung auf Nullwerte durchgeführt wurde:
quelle
Wenn
compareParents(s1, s2) == -1
danncompareParents(s2, s1) == 1
erwartet wird. Mit Ihrem Code ist es nicht immer wahr.Speziell wenn
s1.getParent() == s2 && s2.getParent() == s1
. Es ist nur eines der möglichen Probleme.quelle
Das Bearbeiten der VM-Konfiguration hat bei mir funktioniert.
quelle
-
wie die vorgeschlagene Lösung beginnen soll. Vielleicht wollten Sie stattdessen so etwas wie eine Aufzählungsliste mit einem Punkt.Sie können Objektdaten nicht wie
s1.getParent() == s2
folgt vergleichen : - Dadurch werden die Objektreferenzen verglichen. Sie sollten dieequals function
Foo-Klasse überschreiben und sie dann so vergleichens1.getParent().equals(s2)
quelle