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 > B
和B > C
那么对于任何A
, B
和C
: A > C
我用纸和铅笔检查过,你的代码似乎有几个漏洞:
if (card1.getRarity() < card2.getRarity()) { return 1;
如果card1.getRarity() > card2.getRarity()
则不返回-1
。
if (card1.getId() == card2.getId()) { //... } return -1;
如果ids不相等,则返回-1
。 您应该返回-1
或1
取决于哪个更大的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.sort
和java.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 > o2
, o1 > o2
, o2 > 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以比较()。 现在似乎工作。