Collections.sort() throws Метод сравнения нарушает общий контракт! исключение

Я пытаюсь отсортировать объект List < > , и я получаю это исключение (только для больших списков)

код сортировки:

List<FinalSentence> sentenceList = finalRepresentation.getSentences();
Collections.sort(sentenceList); // <=== EXCEPTION THROWN HERE!!!

Заголовок класса FinalSentence:

public class FinalSentence implements Comparable<FinalSentence>{...}

compareTo():

@Override
public int compareTo(FinalSentence o) {
    if (this == o) {
        return 0;
    }
    if (this.score > o.score) {
        return 1;
    }
    if (this.score < o.score) {
        return -1;
    }
    return 0;
}

это исключение:

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeHi(Unknown Source)
at java.util.ComparableTimSort.mergeAt(Unknown Source)
at java.util.ComparableTimSort.mergeCollapse(Unknown Source)
at java.util.ComparableTimSort.sort(Unknown Source)
at java.util.ComparableTimSort.sort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at feature.finalRepresentation.Summarizer.summarize(Summarizer.java:30)
at driver.Driver.main(Driver.java:114)

для небольшого списка (менее 50 элементов) он работает. для большого списка (он должен работать с теми же), он выдает это исключение. Тип экземпляра List - это ArrayList, но не важно.

Я понятия не имею, как это понять. Список заполнен, элементы одного типа (там нет полиморфизма), и все же я получаю это странное исключение для больших списков.

Любые идеи?

Спасибо!

Ответы

Ответ 1

В соответствии с комментарием, моим предложением использования

Double.compare(score, o.score)

исправлена ​​проблема. Я предполагаю, что была проблема с ±0 или NaN s. Фактически, если вы посмотрите на источник Double.compare(), вы обнаружите, что он немного сложнее, чем вы могли бы подумать, и рассматривал эти случаи конкретно:

958    public static int compare(double d1, double d2) {
959        if (d1 < d2)
960            return -1;           // Neither val is NaN, thisVal is smaller
961        if (d1 > d2)
962            return 1;            // Neither val is NaN, thisVal is larger
963
964        long thisBits = Double.doubleToLongBits(d1);
965        long anotherBits = Double.doubleToLongBits(d2);
966
967        return (thisBits == anotherBits ?  0 : // Values are equal
968                (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
969                 1));                          // (0.0, -0.0) or (NaN, !NaN)
970    }

(источник)

Мораль: будьте осторожны при сравнении удвоений!:)


Справка:

Ответ 2

Это может произойти, если вы нарушите правило транзитивности. Если A > B и B > C, то C > A нарушает договор

Ответ 3

Разве вы не написали:

    if (this.score == o.score) {
        return 0;
    }

вместо этого:

    if (this == o) {
        return 0;
    }

?