Как заставить метод сравнения соблюдать общий договор?
Хромосома содержит ряд баллов, сгенерированных разными способами. Метод compareTo фактически проверяет соответствие методов и, соответственно, возвращает результат.
возврат 1: комп = -5..- 1
return 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: метод сравнения нарушает его общий контракт!
3 ответа
Чтобы немного уточнить ответ сэра Ротна:
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;
}
Если вы реализуете интерфейс Comparator, то вам нужно использовать этот метод (учитывая, что ваш класс является общим для tpye Chromosome):
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>
В противном случае аргументы должны быть Object, а не Chromosome.
Кажется, что вы пытаетесь реализовать, что одна хромосома больше другой, если у нее больше баллов, чем у другой. К сожалению, это не обеспечивает четкого приоритета. Т.е. вы не можете гарантировать, что каждое из o1 = o2 и o2 = o3 o1 = o3 верно. Это может привести к бесконечному циклу упорядочения или, используя более продвинутый алгоритм, исключений, с которыми вы сталкиваетесь. Поэтому вам нужно найти другой алгоритм, обеспечивающий стабильную сортировку.
Подходы:
- Сравните сумму баллов
- Определите приоритет оценки (оценка 2 сравнивается, только если оценка равна 1 и т. Д.)