Java error: Comparison method violates its general contract

前端 未结 10 2149
别跟我提以往
别跟我提以往 2020-11-22 04:17

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 ca

相关标签:
10条回答
  • 2020-11-22 05:07

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

    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!

    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.

    0 讨论(0)
  • 2020-11-22 05:09

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

    After reading on StackOverflow, 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.

    0 讨论(0)
  • 2020-11-22 05:10

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

    /**
     * @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.

    0 讨论(0)
  • I ran into a similar problem where I was trying to sort a n x 2 2D array named contests which is a 2D array of simple integers. This was working for most of the times but threw a runtime error for one 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)
    

    Looking at the answers above I tried adding a condition for equals and I don't know why but it worked. Hopefully we must explicitly specify what should be returned for all cases (greater than, equals and less than):

            Arrays.sort(contests, (row1, row2) -> {
                if (row1[0] < row2[0]) {
                    return 1;
                }
                if(row1[0] == row2[0]) return 0;
                return -1;
            });
    
    0 讨论(0)
  • 2020-11-22 05:13
            if (card1.getRarity() < card2.getRarity()) {
                return 1;
    

    However, if card2.getRarity() is less than card1.getRarity() you might not return -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;
    
        }
    }
    
    0 讨论(0)
  • 2020-11-22 05:15

    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.

    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.

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

    System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
    
    0 讨论(0)
提交回复
热议问题