java错误:comparison方法违反了它的一般约定

o4hqfura  于 2021-06-29  发布在  Java
关注(0)|答案(10)|浏览(503)

我看到了很多关于这个的问题,并试图解决这个问题,但经过一个小时的谷歌搜索和大量的尝试和错误,我仍然无法解决它。我希望你们中的一些人能理解这个问题。
这就是我得到的:

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;
    }
}

你知道吗?

vnjpjtjt

vnjpjtjt1#

我在下面的一个类中遇到了同样的错误 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);
    }
}

得到相同错误后:
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);

修复了错误。

9udxz4iz

9udxz4iz2#

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;

    }
}
4ngedf3f

4ngedf3f3#

考虑以下情况:
第一, 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() . 再次返回1。
从那以后, o1 > o2 ,和 o2 > o1 . 你违反了合同。

doinxwow

doinxwow4#

它还与jdk的版本有关。如果它在jdk6中做得很好,可能会出现您描述的JDK7中的问题,因为JDK7中的实现方法已经改变了。
看看这个:
description:使用的排序算法 java.util.Arrays.sort 以及(间接地)通过 java.util.Collections.sort 已被替换。新的排序实现可能会引发 IllegalArgumentException 如果它检测到 Comparable 这违反了法律 Comparable 合同。以前的实现默默地忽略了这种情况。如果需要以前的行为,可以使用新的系统属性, java.util.Arrays.useLegacyMergeSort ,以恢复以前的合并排序行为。
我不知道确切的原因。但是,如果在使用sort之前添加代码。会没事的。

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

tf7tbtn25#

我遇到了一个类似的问题,当时我正试图解决一个问题 n x 2 2D array 命名 contests 它是一个简单整数的二维数组。这在大多数情况下都有效,但有一次抛出了运行时错误input:-

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

error:-

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;
        });
a7qyws3x

a7qyws3x6#

我也有同样的症状。对我来说,当排序在流中进行时,另一个线程正在修改比较的对象。为了解决这个问题,我将对象Map到不可变的临时对象,将流收集到临时集合,并对其进行排序。

wixjitnu

wixjitnu7#

我必须根据几个标准(日期,如果是同一个日期;其他事情。用旧版本的java在eclipse上工作的东西,在android上不再工作了:比较方法违反了契约。。。
在阅读了stackoverflow之后,我编写了一个单独的函数,如果日期相同,则从compare()调用该函数。此函数根据条件计算优先级,并返回-1、0或1进行比较()。它现在似乎起作用了。

liwlm1x9

liwlm1x98#

也可能是openjdk的错误(不是在这种情况下,但它是相同的错误)
如果像我这样的人偶然发现了这个关于

java.lang.IllegalArgumentException: Comparison method violates its general contract!

那么它也可能是java版本中的一个bug。我有一个比较器运行了几年,现在在一些应用程序。但它突然停止工作,在所有比较完成后抛出错误(我在返回“0”之前比较了6个属性)。
现在我刚刚发现了openjdk的错误报告:
jdk-8210311型
影响版本:8,11
修复版本:12
https://bugs.openjdk.java.net/browse/jdk-8210311

9nvpjoqh

9nvpjoqh9#

异常消息实际上是非常描述性的。它提到的契约是可传递的:如果 A > B 以及 B > C 那么对于任何 A , B 以及 C : A > C . 我用纸和铅笔检查了一下,你的代码似乎有几个洞:

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

你不回来了 -1 如果 card1.getRarity() > card2.getRarity() .

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

你回来了 -1 如果ID不相等。你应该回来 -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!
js81xvg6

js81xvg610#

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

/**
 * @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) 在失败的代码前面。

相关问题