"Comparison method violates its general contract!"
"Метод сравнения нарушает его общий контракт!"
Кто-нибудь может объяснить мне простыми словами, почему этот код выдает исключение "Метод сравнения нарушает его общий контракт!", и как мне это исправить?
privateintcompareParents(Foo s1, Foo s2) { if (s1.getParent() == s2) return -1; if (s2.getParent() == s1) return1; return0; }
Переведено автоматически
Ответ 1
Ваш компаратор не является транзитивным.
Пусть A быть родительским для B, и B быть родительским для C. Поскольку A > B и B > C, то должно быть так, что A > C. Однако, если ваш компаратор вызывается на A и C, он вернет ноль, что означает A == C. Это нарушает контракт и, следовательно, создает исключение.
Со стороны библиотеки довольно мило обнаружить это и сообщить вам, а не вести себя хаотично.
Один из способов удовлетворить требование транзитивности в compareParents() - пройти по getParent() цепочке вместо того, чтобы смотреть только на непосредственного предка.
Ответ 2
Просто потому, что это то, что я получил, когда погуглил эту ошибку, моя проблема заключалась в том, что у меня
на самом деле value >= other.value должно (очевидно) быть value > other.value, чтобы вы действительно могли возвращать 0 с равными объектами.
Ответ 3
Нарушение контракта часто означает, что компаратор не предоставляет правильное или согласованное значение при сравнении объектов. Например, вы можете захотеть выполнить сравнение строк и принудительно отсортировать пустые строки до конца с помощью:
if ( one.length() == 0 ) { return1; // empty string sorts last } if ( two.length() == 0 ) { return -1; // empty string sorts last } return one.compareToIgnoreCase( two );
Но при этом не учитывается случай, когда и один, и два пусты - и в этом случае возвращается неправильное значение (1 вместо 0, чтобы показать совпадение), и компаратор сообщает об этом как о нарушении. Это должно было быть написано как:
if ( one.length() == 0 ) { if ( two.length() == 0 ) { return0; // BOth empty - so indicate } return1; // empty string sorts last } if ( two.length() == 0 ) { return -1; // empty string sorts last } return one.compareToIgnoreCase( two );
Ответ 4
Even if your compareTo is holds transitivity in theory, sometimes subtle bugs mess things up... such as floating point arithmetic error. It happened to me. this was my code:
publicintcompareTo(tfidfContainer compareTfidf) { //descending order if (this.tfidf > compareTfidf.tfidf) return -1; elseif (this.tfidf < compareTfidf.tfidf) return1; else return0;
}
The transitive property clearly holds, but for some reason I was getting the IllegalArgumentException. And it turns out that due to tiny errors in floating point arithmetic, the round-off errors where causing the transitive property to break where they shouldn't! So I rewrote the code to consider really tiny differences 0, and it worked:
publicintcompareTo(tfidfContainer compareTfidf) { //descending order if ((this.tfidf - compareTfidf.tfidf) < .000000001) return0; if (this.tfidf > compareTfidf.tfidf) return -1; elseif (this.tfidf < compareTfidf.tfidf) return1; return0; }