Как заставить метод compareTo соблюдать общий контракт?

Хромосома содержит ряд счетов, полученных различными способами. Метод compareTo фактически проверяет соответствие методов и, соответственно, возвращает результат.

возврат 1: комп = -5..-1

вернуть 0: comp = 0 (может произойти в разных сценариях, один из которых заключается в том, что все оценки равны.

возврат -1: комп = 1..5

public int compareTo(Chromosome o) {
    if(o == null)
        return(1);
    int comp = 0;
    comp += Double.compare(getScore(1),o.getScore(1));
    comp += Double.compare(getScore(2),o.getScore(2));
    comp += Double.compare(getScore(3),o.getScore(3));
    comp += Double.compare(getScore(5),o.getScore(5));
    comp += Double.compare(getScore(7),o.getScore(7));
    if(comp == 0)
        return(0);
    if(comp > 0)
        return(1);
    else
        return(-1);
}

Мой вопрос заключается в том, как заставить этот сценарий соответствовать правилам, установленным контрактом для компаратор. По-видимому, это не так, и я продолжаю получать: java.lang.IllegalArgumentException: метод сравнения нарушает его общий контракт!


person jallmer    schedule 29.05.2013    source источник
comment
Можно ли вызвать более старую версию Collection.sort, не требуя от пользователя установки какого-либо флага в каком-либо java.ini? Кроме того, можно ли сказать JVM игнорировать контракт?   -  person jallmer    schedule 29.05.2013


Ответы (3)


Если вы реализуете интерфейс Comparator, вам нужно использовать этот метод (учитывая, что ваш класс является общим с Chromosome tpye):

int compare(Chromosome o1, Chromosome o2)

Однако кажется, что более подходящим интерфейсом для реализации в вашем случае является Comparable. http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html

int compareTo(Chromosome o)

Comparable обычно реализуется, чтобы дать экземплярам вашего класса естественный порядок. Comparator обычно представляет собой отдельный класс для того, что вы пытаетесь сравнить, и может использоваться для предоставления вам нескольких различных типов упорядочения.

Независимо от того, что вы реализуете, класс также должен быть типизирован:

class Chromosome implements Comparable<Chromosome> 

В противном случае аргументы должны быть Объектом, а не Хромосомой.

person zetches    schedule 29.05.2013
comment
Компаратор придерживается того же контракта? - person jallmer; 29.05.2013
comment
Я так думаю, за исключением того факта, что это разные методы с разными сигнатурами. В обоих случаях вы должны быть согласованы с equals(), даже если это не является строгим требованием в соответствии с JavaDoc. Также рекомендуется, чтобы Comparator реализовал Serializable. Кроме того, оба должны генерировать исключение NullPointerException для нулевого значения. - person zetches; 29.05.2013

Чтобы немного уточнить ответ сэра RotN:

Метод compareTo должен соответствовать двум свойствам:

  • Сравнение симметрично, т. е. если A=B, то B=A, а если A<B, то B>A
  • Сравнение транзитивное, т. е. если A<B и B<C, то A<C, а если A=B и B=C, то A=C

Первое свойство для вашего сравнения выполняется, а второе нет. Рассмотрим следующий пример из теории голосования: у нас есть 3 человека, которые голосуют за 3 альтернативы. Побеждает альтернатива с наивысшим рейтингом. Известно, что это может привести к двусмысленной ситуации, когда никакая альтернатива не побеждает.


В вашем случае баллы — это люди, а хромосомы — это альтернативы. Вместо 5 баллов я использую только 3, так как этого достаточно, чтобы показать проблему. У меня есть 3 хромосомы, A, B и C, со следующими оценками:

A: 1, 2, 3
B: 2, 3, 1
C: 3, 1, 2

Нетрудно заметить, что A<B, B<C, и C<A, поэтому ваше сравнение не является транзитивным.


Вы можете решить эту проблему, упорядочив хромосомы лексикографически:

public int compareTo(Chromosome o) {
    if(o == null)
        return(1);
    int[] indices = {1, 2, 3, 5, 7};
    for (int i : indices) {
        int c = Double.compare(getScore(i),o.getScore(i));
        if (c != 0)
            return c;
    }
    return 0;
}
person Vincent van der Weele    schedule 29.05.2013
comment
@jallmer Это действительно зависит от ваших требований. Важен ли порядок или вам просто нужен какой-то произвольный порядок? - person Vincent van der Weele; 29.05.2013
comment
Идеальный порядок не имеет решающего значения, но он должен быть несколько упорядоченным. - person jallmer; 29.05.2013
comment
@jallmer Тогда вы могли бы, например, сначала добавить все оценки для каждой хромосомы, а затем сравнить две суммы, но обратите внимание, что у вас может быть много пар, которые считаются равными (то есть результат сравнения равен 0) - person Vincent van der Weele; 29.05.2013
comment
Хм, баллы на самом деле не складываются. Я добавляю результат сравнения, разве это не должно привести к аналогичному поведению? - person jallmer; 29.05.2013
comment
@jallmer, к сожалению, нет, как видно из контрпримера. Вы пытаетесь сравнить 2 точки в многомерном пространстве. Добавление их в основном сравнило бы их манхэттенское расстояние с исходной точкой. Вы также можете суммировать квадраты каждой оценки и извлечь квадратный корень, чтобы получить евклидово расстояние до начала координат, но это, вероятно, тоже не поможет. Тогда я бы предложил лексикографический порядок. Я обновлю свой ответ примером кода. - person Vincent van der Weele; 29.05.2013
comment
Спасибо за пример кода, я изменил порядок индексов в соответствии с вероятностью успеха отдельных оценок. При простом суммировании баллов я преуспеваю в 40%, при использовании предоставленного вами метода это 37%, если ошибка не будет выдана, я преуспею в 55%. Я могу проверить это на некоторых экспериментальных данных, которые у меня есть. - person jallmer; 29.05.2013

То, что вы пытаетесь реализовать, кажется, что одна хромосома лучше другой, если у нее больше баллов, которые больше, чем у другой. К сожалению, это не дает явного приоритета. т.е. вы не можете гарантировать, что каждое o1 = o2 и o2 = o3 o1 = o3 истинно. Это может привести к бесконечному циклу при заказе или, используя более продвинутый алгоритм, к исключению, с которым вы сталкиваетесь. Поэтому вам нужно найти другой алгоритм, обеспечивающий стабильную сортировку.

Подходы:

  1. Сравните сумму баллов
  2. Определить приоритет оценки (оценка2 сравнивается только в том случае, если оценка1 равна и т. д.)
person Sir RotN    schedule 29.05.2013
comment
Спасибо, 1) кажется, что я делаю, не так ли? Я суммирую результаты отдельных сравнений, которые должны быть прямыми, но, похоже, не работают должным образом. - person jallmer; 29.05.2013