Java错误:比较方法违反了它的一般约定

时间:2021-03-09 22:50:19

I saw many questions about this, and tried to solve the problem, but after one hour of googling and a lots of trial & error, I still can't fix it. I hope some of you catch the problem.

我看到了很多关于这个问题的问题,并试图解决这个问题,但是在google了一个小时的搜索和大量的尝试和错误之后,我仍然无法修复它。我希望你们中有人能理解这个问题。

This is what I get:

这就是我得到的:

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)
    ...

And this is my comparator:

这是我的比较仪

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

Any idea?

任何想法?

7 个解决方案

#1


64  

The exception message is actually pretty descriptive. The contract it mentions is transitivity: if A > B and B > C then for any A, B and C: A > C. I checked it with paper and pencil and your code seems to have few holes:

异常消息实际上是相当描述性的。它提到的契约是及物性的:如果一个> B和B > C那么对于任意一个A, B和C:一个> C。

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

you do not return -1 if card1.getRarity() > card2.getRarity().

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


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

You return -1 if ids aren't equal. You should return -1 or 1 depending on which id was bigger.

如果id不相等,返回-1。您应该返回-1或1,这取决于哪个id更大。


Take a look at this. Apart from being much more readable, I think it should actually work:

看看这个。除了可读性强得多之外,我认为它实际上应该是有用的:

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!

#2


26  

It also has something to do with the version of JDK. If it does well in JDK6, maybe it will have the problem in JDK 7 described by you, because the implementation method in jdk 7 has been changed.

它还与JDK的版本有关。如果它在JDK6中表现良好,那么您可能会在JDK 7中遇到问题,因为JDK 7中的实现方法已经更改。

Look at this:

看看这个:

Description: The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. The previous implementation silently ignored such a situation. If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, to restore previous mergesort behaviour.

描述:java.util. array使用的排序算法。通过java.util.Collections排序和(间接)。所取代。如果新类型实现检测到违反可比契约的可比类,则可能抛出一个非法争论的异常。前面的实现默默地忽略了这种情况。如果需要前面的行为,可以使用新的system属性java.util. array。useLegacyMergeSort,恢复以前的合并行为。

I don't know the exact reason. However, if you add the code before you use sort. It will be OK.

我不知道确切的原因。但是,如果在使用sort之前添加代码。这将是好的。

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

#3


21  

You can use the following class to pinpoint transitivity bugs in your Comparators:

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

/**
 * @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()
    {
    }
}

Simply invoke Comparators.verifyTransitivity(myComparator, myCollection) in front of the code that fails.

简单地调用比较器。在失败的代码前验证传递性(myComparator, myCollection)。

#4


5  

Consider the following case:

考虑下面的例子:

First, o1.compareTo(o2) is called. card1.getSet() == card2.getSet() happens to be true and so is card1.getRarity() < card2.getRarity(), so you return 1.

首先,o1.compareTo(o2)。这是一个很好的例子,它是一个很好的例子,它是一个很好的例子。

Then, o2.compareTo(o1) is called. Again, card1.getSet() == card2.getSet() is true. Then, you skip to the following else, then card1.getId() == card2.getId() happens to be true, and so is cardType > item.getCardType(). You return 1 again.

然后,o2.compareTo(o1)。同样,card1.getSet() = card2.getSet()是正确的。然后,跳到下面的else语句,然后card1.getId() = card2.getId()恰好为真,cardType > item.getCardType()也是。你又返回1。

From that, o1 > o2, and o2 > o1. You broke the contract.

o1 > o2, o2 > o1。你违反了合同。

#5


2  

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

However, if card2.getRarity() is less than card1.getRarity() you might not return -1.

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

You similarly miss other cases. I would do this, you can change around depending on your intent:

你也同样错过了其他案例。我会这么做,你可以根据你的意图来改变:

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;

    }
}

#6


0  

I had to sort on several criterion (date, and, if same date; other things...). What was working on Eclipse with an older version of Java, did not worked any more on Android : comparison method violates contract ...

我必须根据几个标准(日期,如果是同样的日期;其他的事情……)。用旧版本的Java在Eclipse上工作的东西,在Android上就不再工作了:比较方法违反了约定……

After reading on *, I wrote a separate function that I called from compare() if the dates are the same. This function calculates the priority, according to the criteria, and returns -1, 0, or 1 to compare(). It seems to work now.

在阅读*之后,我编写了一个单独的函数,如果日期相同,我从compare()调用它。该函数根据条件计算优先级,并返回-1、0或1进行比较()。它现在似乎起作用了。

#7


0  

I got the same error with a class like the following StockPickBean. Called from this code:

我在一个类中也犯了同样的错误,比如下面的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);
    }
}

After getting the same error:

在得到相同的错误后:

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

. lang。非法辩论例外:比较法违反了它的一般合同!

I changed this line:

我改变了这条线:

public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int) 
         (val1.value - val2.value);

to:

:

public static Comparator<StockPickBean> VALUE = (StockPickBean spb1, 
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);

That fixes the error.

修复错误。

#1


64  

The exception message is actually pretty descriptive. The contract it mentions is transitivity: if A > B and B > C then for any A, B and C: A > C. I checked it with paper and pencil and your code seems to have few holes:

异常消息实际上是相当描述性的。它提到的契约是及物性的:如果一个> B和B > C那么对于任意一个A, B和C:一个> C。

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

you do not return -1 if card1.getRarity() > card2.getRarity().

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


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

You return -1 if ids aren't equal. You should return -1 or 1 depending on which id was bigger.

如果id不相等,返回-1。您应该返回-1或1,这取决于哪个id更大。


Take a look at this. Apart from being much more readable, I think it should actually work:

看看这个。除了可读性强得多之外,我认为它实际上应该是有用的:

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!

#2


26  

It also has something to do with the version of JDK. If it does well in JDK6, maybe it will have the problem in JDK 7 described by you, because the implementation method in jdk 7 has been changed.

它还与JDK的版本有关。如果它在JDK6中表现良好,那么您可能会在JDK 7中遇到问题,因为JDK 7中的实现方法已经更改。

Look at this:

看看这个:

Description: The sorting algorithm used by java.util.Arrays.sort and (indirectly) by java.util.Collections.sort has been replaced. The new sort implementation may throw an IllegalArgumentException if it detects a Comparable that violates the Comparable contract. The previous implementation silently ignored such a situation. If the previous behavior is desired, you can use the new system property, java.util.Arrays.useLegacyMergeSort, to restore previous mergesort behaviour.

描述:java.util. array使用的排序算法。通过java.util.Collections排序和(间接)。所取代。如果新类型实现检测到违反可比契约的可比类,则可能抛出一个非法争论的异常。前面的实现默默地忽略了这种情况。如果需要前面的行为,可以使用新的system属性java.util. array。useLegacyMergeSort,恢复以前的合并行为。

I don't know the exact reason. However, if you add the code before you use sort. It will be OK.

我不知道确切的原因。但是,如果在使用sort之前添加代码。这将是好的。

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

#3


21  

You can use the following class to pinpoint transitivity bugs in your Comparators:

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

/**
 * @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()
    {
    }
}

Simply invoke Comparators.verifyTransitivity(myComparator, myCollection) in front of the code that fails.

简单地调用比较器。在失败的代码前验证传递性(myComparator, myCollection)。

#4


5  

Consider the following case:

考虑下面的例子:

First, o1.compareTo(o2) is called. card1.getSet() == card2.getSet() happens to be true and so is card1.getRarity() < card2.getRarity(), so you return 1.

首先,o1.compareTo(o2)。这是一个很好的例子,它是一个很好的例子,它是一个很好的例子。

Then, o2.compareTo(o1) is called. Again, card1.getSet() == card2.getSet() is true. Then, you skip to the following else, then card1.getId() == card2.getId() happens to be true, and so is cardType > item.getCardType(). You return 1 again.

然后,o2.compareTo(o1)。同样,card1.getSet() = card2.getSet()是正确的。然后,跳到下面的else语句,然后card1.getId() = card2.getId()恰好为真,cardType > item.getCardType()也是。你又返回1。

From that, o1 > o2, and o2 > o1. You broke the contract.

o1 > o2, o2 > o1。你违反了合同。

#5


2  

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

However, if card2.getRarity() is less than card1.getRarity() you might not return -1.

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

You similarly miss other cases. I would do this, you can change around depending on your intent:

你也同样错过了其他案例。我会这么做,你可以根据你的意图来改变:

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;

    }
}

#6


0  

I had to sort on several criterion (date, and, if same date; other things...). What was working on Eclipse with an older version of Java, did not worked any more on Android : comparison method violates contract ...

我必须根据几个标准(日期,如果是同样的日期;其他的事情……)。用旧版本的Java在Eclipse上工作的东西,在Android上就不再工作了:比较方法违反了约定……

After reading on *, I wrote a separate function that I called from compare() if the dates are the same. This function calculates the priority, according to the criteria, and returns -1, 0, or 1 to compare(). It seems to work now.

在阅读*之后,我编写了一个单独的函数,如果日期相同,我从compare()调用它。该函数根据条件计算优先级,并返回-1、0或1进行比较()。它现在似乎起作用了。

#7


0  

I got the same error with a class like the following StockPickBean. Called from this code:

我在一个类中也犯了同样的错误,比如下面的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);
    }
}

After getting the same error:

在得到相同的错误后:

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

. lang。非法辩论例外:比较法违反了它的一般合同!

I changed this line:

我改变了这条线:

public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int) 
         (val1.value - val2.value);

to:

:

public static Comparator<StockPickBean> VALUE = (StockPickBean spb1, 
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);

That fixes the error.

修复错误。