Java错误:比较方法违反了它的一般合同

我看到很多关于这个的问题,并试图解决这个问题,但经过一个小时的谷歌search和大量的试验和错误,我仍然无法修复。 我希望你们中的一些人能够解决问题。

这是我得到的:

java.lang.IllegalArgumentException: Comparison method violates its general contract! at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835) at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453) at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392) at java.util.ComparableTimSort.sort(ComparableTimSort.java:191) at java.util.ComparableTimSort.sort(ComparableTimSort.java:146) at java.util.Arrays.sort(Arrays.java:472) at java.util.Collections.sort(Collections.java:155) ... 

这是我的比较:

 @Override public int compareTo(Object o) { if(this == o){ return 0; } CollectionItem item = (CollectionItem) o; Card card1 = CardCache.getInstance().getCard(cardId); Card card2 = CardCache.getInstance().getCard(item.getCardId()); if (card1.getSet() < card2.getSet()) { return -1; } else { if (card1.getSet() == card2.getSet()) { if (card1.getRarity() < card2.getRarity()) { return 1; } else { if (card1.getId() == card2.getId()) { if (cardType > item.getCardType()) { return 1; } else { if (cardType == item.getCardType()) { return 0; } return -1; } } return -1; } } return 1; } } 

任何想法?

exception消息实际上是相当具有描述性的。 它提到的合同是传递 :如果A > BB > C那么对于任何ABCA > C 我用纸和铅笔检查过,你的代码似乎有几个漏洞:

 if (card1.getRarity() < card2.getRarity()) { return 1; 

如果card1.getRarity() > card2.getRarity()则不返回-1


 if (card1.getId() == card2.getId()) { //... } return -1; 

如果ids不相等,则返回-1 。 您应该返回-11取决于哪个更大的ID。


看看这个。 除了更可读,我认为它应该实际上工作:

 if (card1.getSet() > card2.getSet()) { return 1; } if (card1.getSet() < card2.getSet()) { return -1; }; if (card1.getRarity() < card2.getRarity()) { return 1; } if (card1.getRarity() > card2.getRarity()) { return -1; } if (card1.getId() > card2.getId()) { return 1; } if (card1.getId() < card2.getId()) { return -1; } return cardType - item.getCardType(); //watch out for overflow! 

它也与JDK的版本有关。 如果它在JDK6中performance良好,也许在你描述的JDK 7中会有问题,因为jdk 7中的实现方法已经改变了。

看这个:

说明:由java.util.Arrays.sortjava.util.Arrays.sort (间接)使用的sortingalgorithm已被replace。 如果新的sorting实现检测到违反Comparable合约的Comparable ,则可能会抛出IllegalArgumentException Comparable 。 以前的实施默默地忽略了这种情况。 如果需要以前的行为,则可以使用新的系统属性java.util.Arrays.useLegacyMergeSort来恢复以前的mergesort行为。

我不知道确切的原因。 但是,如果在使用sorting之前添加代码。 一切都会安好的。

 System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 

您可以使用以下类来查找比较器中的传递性错误:

 /** * @author Gili Tzabari */ public final class Comparators { /** * Verify that a comparator is transitive. * * @param <T> the type being compared * @param comparator the comparator to test * @param elements the elements to test against * @throws AssertionError if the comparator is not transitive */ public static <T> void verifyTransitivity(Comparator<T> comparator, Collection<T> elements) { for (T first: elements) { for (T second: elements) { int result1 = comparator.compare(first, second); int result2 = comparator.compare(second, first); if (result1 != -result2) { // Uncomment the following line to step through the failed case //comparator.compare(first, second); throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 + " but swapping the parameters returns " + result2); } } } for (T first: elements) { for (T second: elements) { int firstGreaterThanSecond = comparator.compare(first, second); if (firstGreaterThanSecond <= 0) continue; for (T third: elements) { int secondGreaterThanThird = comparator.compare(second, third); if (secondGreaterThanThird <= 0) continue; int firstGreaterThanThird = comparator.compare(first, third); if (firstGreaterThanThird <= 0) { // Uncomment the following line to step through the failed case //comparator.compare(first, third); throw new AssertionError("compare(" + first + ", " + second + ") > 0, " + "compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " + firstGreaterThanThird); } } } } } /** * Prevent construction. */ private Comparators() { } } 

只需在失败的代码前调用Comparators.verifyTransitivity(myComparator, myCollection)

考虑以下情况:

首先, o1.compareTo(o2)card1.getSet() == card2.getSet()碰巧是true,所以是card1.getRarity() < card2.getRarity() ,所以你返回1。

然后, o2.compareTo(o1) 。 再次, card1.getSet() == card2.getSet()是true。 然后,你跳到下面的else ,然后card1.getId() == card2.getId()恰好是true, cardType > item.getCardType()也是cardType > item.getCardType() 。 你再次返回1。

o1 > o2o1 > o2o2 > o1 。 你打破了合同。

  if (card1.getRarity() < card2.getRarity()) { return 1; 

但是,如果card2.getRarity()小于card1.getRarity() ,则可能不会返回-1

您同样错过其他情况。 我会这样做,你可以根据你的意图改变:

 public int compareTo(Object o) { if(this == o){ return 0; } CollectionItem item = (CollectionItem) o; Card card1 = CardCache.getInstance().getCard(cardId); Card card2 = CardCache.getInstance().getCard(item.getCardId()); int comp=card1.getSet() - card2.getSet(); if (comp!=0){ return comp; } comp=card1.getRarity() - card2.getRarity(); if (comp!=0){ return comp; } comp=card1.getSet() - card2.getSet(); if (comp!=0){ return comp; } comp=card1.getId() - card2.getId(); if (comp!=0){ return comp; } comp=card1.getCardType() - card2.getCardType(); return comp; } } 

我不得不根据几个标准(date,如果是相同的date,其他的东西…)。 在Eclipse上使用旧版Java的工作,在Android上没有工作:比较方法违反契约…

在读完stackoverflow之后,我写了一个独立的函数,如果date相同的话,我会从compare()调用它。 此函数根据标准计算优先级,并回退-1,0或1以比较()。 现在似乎工作。