我看到很多关于此的问题,并试图解决这个问题,但经过一个小时的谷歌搜索和大量的试验和错误,我仍然无法解决它。我希望你们中的一些人能够解决这个问题。
这就是我得到的:
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;
}
}
有什么想法吗?
答案 0 :(得分:76)
异常消息实际上非常具有描述性。它提到的合同是传递性:如果A > B
和B > C
那么任何A
,B
和C
:{{1 }}。我用纸和笔检查了它,你的代码似乎有几个漏洞:
A > C
如果if (card1.getRarity() < card2.getRarity()) {
return 1;
,则不会返回-1
。
card1.getRarity() > card2.getRarity()
如果ID不相等,则返回if (card1.getId() == card2.getId()) {
//...
}
return -1;
。您应该返回-1
或-1
,具体取决于哪个ID更大。
看看这个。除了更具可读性之外,我认为它实际上应该有效:
1
答案 1 :(得分:29)
您可以使用以下类来查明比较器中的传递性错误:
/**
* @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)
。
答案 2 :(得分:28)
它还与JDK版本有关。 如果它在JDK6中运行良好,可能会在您描述的JDK 7中出现问题,因为jdk 7中的实现方法已经改变。
看看这个:
描述:java.util.Arrays.sort
和({间接)java.util.Collections.sort
使用的排序算法已被替换。如果新排序实施检测到违反IllegalArgumentException
合同的Comparable
,则可能会抛出Comparable
。以前的实现默默地忽略了这种情况。如果需要先前的行为,则可以使用新的系统属性java.util.Arrays.useLegacyMergeSort
来恢复先前的mergesort行为。
我不知道具体原因。但是,如果在使用sort之前添加代码。没关系。
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
答案 3 :(得分:6)
考虑以下情况:
首先,调用o1.compareTo(o2)
。 card1.getSet() == card2.getSet()
恰好是真的,card1.getRarity() < card2.getRarity()
也是如此,所以你返回1。
然后,调用o2.compareTo(o1)
。同样,card1.getSet() == card2.getSet()
是真的。然后,您跳到以下else
,然后card1.getId() == card2.getId()
恰好为真,cardType > item.getCardType()
也是如此。你又回来了。
由此,o1 > o2
和o2 > o1
。你违反了合同。
答案 4 :(得分:2)
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;
}
}
答案 5 :(得分:1)
我有相同的症状。对我来说,事实证明,在Stream中进行排序时,另一个线程正在修改比较的对象。为了解决该问题,我将对象映射到不可变的临时对象,将Stream收集到一个临时Collection中,并对该对象进行了排序。
答案 6 :(得分:0)
我不得不排序几个标准(日期,如果相同的日期;其他事情......)。使用旧版Java的Eclipse工作原理在Android上没有用处:比较方法违反合同......
在阅读StackOverflow之后,我写了一个单独的函数,如果日期相同,我会从compare()调用。此函数根据条件计算优先级,并返回-1,0或1以进行比较()。它似乎现在工作。
答案 7 :(得分:0)
我遇到类似以下StockPickBean
的类错误。从这段代码中调用:
List<StockPickBean> beansListcatMap.getValue();
beansList.sort(StockPickBean.Comparators.VALUE);
public class StockPickBean implements Comparable<StockPickBean> {
private double value;
public double getValue() { return value; }
public void setValue(double value) { this.value = value; }
@Override
public int compareTo(StockPickBean view) {
return Comparators.VALUE.compare(this,view); //return
Comparators.SYMBOL.compare(this,view);
}
public static class Comparators {
public static Comparator<StockPickBean> VALUE = (val1, val2) ->
(int)
(val1.value - val2.value);
}
}
收到同样的错误后:
java.lang.IllegalArgumentException:比较方法违反了其一般合同!
我更改了这一行:
public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int)
(val1.value - val2.value);
为:
public static Comparator<StockPickBean> VALUE = (StockPickBean spb1,
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);
修正错误。
答案 8 :(得分:0)
这也可能是OpenJDK错误...(在这种情况下不是,但这是相同的错误)
如果像我这样的人偶然发现了关于
的答案java.lang.IllegalArgumentException: Comparison method violates its general contract!
那么这也可能是Java版本中的错误。我有一个compareTo自几年以来在某些应用程序中运行。但是突然之间,它停止了工作,并在所有比较完成后抛出错误(我在返回“ 0”之前比较了6个属性)。
现在我刚刚找到了OpenJDK的这个Bugreport:
答案 9 :(得分:0)
我遇到了类似的问题,我试图对名为n x 2 2D array
的{{1}}进行排序,该contests
是2D的简单整数数组。这在大多数时候都有效,但是对一个输入抛出了运行时错误:-
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;
});