2013-10-11 4 views
36

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

Любая помощь будет принята с благодарностью

public int compare(Node o1, Node o2) 
{ 
    HashMap<Integer,Integer> childMap = orderMap.get(parentID); 
    if(childMap != null && childMap.containsKey(o1.getID()) && 
          childMap.containsKey(o2.getID())) 
    { 
     int order1 = childMap.get(o1.getID()); 
     int order2 = childMap.get(o2.getID()); 

     if(order1<order2) 
      return -1; 
     else if(order1>order2) 
      return 1; 
     else 
      return 0; 
    } 
    else 
     return 0; 
} 

Добавление исключение я получаю

java.lang.IllegalArgumentException: Comparison method violates its general contract! 
at java.util.TimSort.mergeLo(TimSort.java:747) 
at java.util.TimSort.mergeAt(TimSort.java:483) 
at java.util.TimSort.mergeCollapse(TimSort.java:410) 
at java.util.TimSort.sort(TimSort.java:214) 
at java.util.TimSort.sort(TimSort.java:173) 
at java.util.Arrays.sort(Arrays.java:659) 
at java.util.Collections.sort(Collections.java:217) 
+0

В какой строке вы получаете исключение? –

+0

@tieTYT Я думаю, что исключение выбрасывается из коллекции, которую он передает в качестве «компаратора». Этот компаратор выглядит действительно неустойчивым, так как любые изменения в * 'orderMap [parentID]' * или порядок, указанный в 'childMap', будут перемещать элементы вокруг. – chrylis

+0

Что такое тип 'parentId'? Вы сохраняете HashMap на карте? –

ответ

49

Ваш метод compare() является не транзитивным. Если A == B и B == C, то A должно быть равно C.

Теперь рассмотрим этот случай:

Для A, B и C, предположим, что метод containsKey() возвращать эти результаты:

  • childMap.containsKey(A.getID()) возвращается true
  • childMap.containsKey(B.getID()) возвращает false
  • childMap.containsKey(C.getID()) возвращается true

Кроме того, закажите поставку для A.getId()! = B.getId().

Так,

  1. A и B вернется 0, так как внешний if условие будет false =>A == B
  2. B и C вернутся 0, как наружный if условие будет false =>B == C

Но, A и C, могут вернуть -1, или 1, исходя из вашего теста внутри блока if. Итак, A != C. Это нарушает принцип транзитивности.

Я думаю, вы должны добавить некоторые условия внутри вашего блока else, который выполняет проверку аналогично тому, как вы делаете в блоке if.

3

Я думаю, проблема в вашем случае по умолчанию. Рассмотрим множество узлов A, B и C, где идентификаторы: 'a', 'b' и 'c'.Рассмотрим далее, что ваш childMap, который содержит информацию относительно заказа, имеет следующее содержание:

{ 'a' => 1, 'c' => 3 } 

Теперь, если вы запустите compare метод на А и В, вы вернетесь 0, указывая, что А и В эквивалентны. Кроме того, если вы сравниваете B и C, вы все равно возвращаете 0. Однако, если вы сравниваете A и C, вы возвращаете -1, что указывает на то, что A меньше. Это нарушает транзитивность свойство the Comparator contract:

The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0 .

Finally, the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z .

Вы не можете лечить «предметы, которые не имеют приказа, назначенный» как имеющее значение «где-то смутно в середине», так как алгоритмы сортировки не знают где их поставить. Если вы хотите остаться с таким подходом, то в случае, когда значение отсутствует на карте, вам нужно назначить фиксированное значение порядковым номером; что-то вроде 0 или MIN_INT - разумный выбор (но любой выбор должен быть задокументирован в Javadoc для compare!).

Смежные вопросы