"Comparison Method Violates Its General Contract!" - Timsort and Gridlayout

Comparison method violates its general contract! - TimSort and GridLayout

It seems to me like you've hit a bug in the JDK since the error seems to come from Swing classes.

Options:

  1. Define the property java.util.Arrays.useLegacyMergeSort as true. Either using in your code the line

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

    before any Swing code. As the first line in the main method should work.

    Or adding

    -Djava.util.Arrays.useLegacyMergeSort=true

    to your starting options (in the console, or in the project properties in an IDE, Ant script, etc.)

  2. Upgrade your JDK and see if the problem goes away

  3. Downgrade to Java 6

Comparison method violates its general contract - how to avoid it

Your comparator doesn't deal with nulls and unparseable dates correctly. Consider the following case:

Suppose you have two non null dates d1 and d2 and a null d3.
Suppose d1 > d2.

You thus have

d1 > d2
d1 == d3
d2 == d3

So, if d1 and d2 are both equal to d3, they should also be equal to each other, but they're not.

Start by transforming all your strings to dates or null.

Then use a comparator which considers all null values as bigger (or lower) than all non null values. Comparator has utility methods to transform a comparator of non-null objects into a comparator which deals with nulls by putting them all first or last.

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!

“Comparison method violates its general contract!”

Both these cases are problematic.

if (lhs == null || rhs == null) return 0;

If you have [123, null, 234], then you compare 123 as equal to null, null as equal to 234, and by transitivity you should get 123 equals 234. But that is not what your comparator returns.

The solution here would be to either disallow null or sort all nulls to the bottom (or top), i.e. only return 0 is both are null, otherwise return 1 or -1 (depending on the null being left or right).

return (int) (lhs.mTaskInfo.time - rhs.mTaskInfo.time);

Consider comparing Integer.MAX_VALUE + 1 to 0. The difference between the two is Integer.MAX_VALUE + 1. Casting this to int wraps to Integer.MIN_VALUE. The opposite comparison should then give you - Integer.MIN_VALUE, but that is Integer.MIN_VALUE again due to overflow.

The solution here is to use Long.compare(a,b).



Related Topics



Leave a reply



Submit