Java错误:比较方法违反其通用合同。

121
我看到很多关于这个问题的提问,尝试解决了这个问题,但经过一小时的搜索和许多试错后,我仍然无法解决它。希望你们中的一些人能够发现问题所在。
这是我得到的:
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;
    }
}

有什么想法吗?


哪一行代码导致了这个异常的抛出?ComparableTimSort.java文件的第835和453行上是什么? - Hovercraft Full Of Eels
2
@HovercraftFullOfEels 这是来自Oracle的一个类,不是我写的。它在那一行抛出了一个异常。这个方法非常长,看起来很难理解。 - Lakatos Gyula
我真的很好奇,是什么让你写出这样奇怪、不对称且难以阅读的“compareTo”函数??? - maaartinus
1
读完《Clean Code》这本书之后,我也不知道了。 - Lakatos Gyula
可能是重复的问题:"Comparison method violates its general contract!" - Gili
显示剩余2条评论
13个回答

1
我遇到了一个类似的问题,我尝试对名为“contests”的n x 2二维数组进行排序,该数组是由简单整数组成的二维数组。这在大多数情况下都有效,但对于一个输入会抛出运行时错误:
Arrays.sort(contests, (row1, row2) -> {
            if (row1[0] < row2[0]) {
                return 1;
            } else return -1;
        });

错误:-
Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.base/java.util.TimSort.mergeHi(TimSort.java:903)
    at java.base/java.util.TimSort.mergeAt(TimSort.java:520)
    at java.base/java.util.TimSort.mergeForceCollapse(TimSort.java:461)
    at java.base/java.util.TimSort.sort(TimSort.java:254)
    at java.base/java.util.Arrays.sort(Arrays.java:1441)
    at com.hackerrank.Solution.luckBalance(Solution.java:15)
    at com.hackerrank.Solution.main(Solution.java:49)

看了上面的答案,我尝试添加了一个equals的条件,不知道为什么,但它起作用了。希望我们必须明确指定所有情况下应该返回什么(大于、等于和小于):

        Arrays.sort(contests, (row1, row2) -> {
            if (row1[0] < row2[0]) {
                return 1;
            }
            if(row1[0] == row2[0]) return 0;
            return -1;
        });

它同样适用于 Collections.sort。 - djdance

0

我必须按照几个标准进行排序(日期,以及如果日期相同,则是其他事情...)。在Eclipse上使用旧版本的Java可以工作,但在Android上不再起作用:比较方法违反了合同...

在StackOverflow上阅读后,我编写了一个单独的函数,如果日期相同,则从compare()中调用该函数。根据标准,此函数计算优先级并返回-1、0或1给compare()。现在似乎可以工作。


0

做一些简单的事情怎么样?就像这样:

int result = card1.getSet().compareTo(card2.getSet())
if (result == 0) {
    result = card1.getRarity().compareTo(card2.getRarity())
}
if (result == 0) {
    result = card1.getId().compareTo(card2.getId())
}
if (result == 0) {
    result = card1.getCardType().compareTo(card2.getCardType())
}
return result;

你只需要按照偏好顺序对比较进行排序即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接