Вопрос-ответ

"Comparison method violates its general contract!"

"Метод сравнения нарушает его общий контракт!"

Кто-нибудь может объяснить мне простыми словами, почему этот код выдает исключение "Метод сравнения нарушает его общий контракт!", и как мне это исправить?

private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
Переведено автоматически
Ответ 1

Ваш компаратор не является транзитивным.

Пусть A быть родительским для B, и B быть родительским для C. Поскольку A > B и B > C, то должно быть так, что A > C. Однако, если ваш компаратор вызывается на A и C, он вернет ноль, что означает A == C. Это нарушает контракт и, следовательно, создает исключение.

Со стороны библиотеки довольно мило обнаружить это и сообщить вам, а не вести себя хаотично.

Один из способов удовлетворить требование транзитивности в compareParents() - пройти по getParent() цепочке вместо того, чтобы смотреть только на непосредственного предка.

Ответ 2

Просто потому, что это то, что я получил, когда погуглил эту ошибку, моя проблема заключалась в том, что у меня

if (value < other.value)
return -1;
else if (value >= other.value)
return 1;
else
return 0;

на самом деле value >= other.value должно (очевидно) быть value > other.value, чтобы вы действительно могли возвращать 0 с равными объектами.

Ответ 3

Нарушение контракта часто означает, что компаратор не предоставляет правильное или согласованное значение при сравнении объектов. Например, вы можете захотеть выполнить сравнение строк и принудительно отсортировать пустые строки до конца с помощью:

if ( one.length() == 0 ) {
return 1; // 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 ) {
return 0; // BOth empty - so indicate
}
return 1; // 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:

public int compareTo(tfidfContainer compareTfidf) {
//descending order
if (this.tfidf > compareTfidf.tfidf)
return -1;
else if (this.tfidf < compareTfidf.tfidf)
return 1;
else
return 0;

}

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:

public int compareTo(tfidfContainer compareTfidf) {
//descending order
if ((this.tfidf - compareTfidf.tfidf) < .000000001)
return 0;
if (this.tfidf > compareTfidf.tfidf)
return -1;
else if (this.tfidf < compareTfidf.tfidf)
return 1;
return 0;
}
2023-11-22 12:53 java