Java error: Comparison method violates its general contract Java error: Comparison method violates its general contract java java

Java error: Comparison method violates its general contract


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:

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

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


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.


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!


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.


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");