Rapiernik
Rapiernik

Reputation: 111

Comparison method violates its general contract - how to avoid it

I know there are probably a few really good explanations of that issue, but I still cannot apply it to my code. How to avoid that error in my case? Here is my code:

        sampleList.sort((o1, o2) -> {
            try {
                if (o1.getDate() == null || o2.getDate() == null) return 0;
                int i = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy").parse(o1.getDate()).compareTo(new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy").parse(o2.getDate()));
                return i;
            } catch (ParseException e) {
                logger.error("Sorting failed", e);
            }
            return 0;
        });

sampleList is a list of type, which contains date in string. And here is an exception I get:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeLo(Unknown Source)
    at java.util.TimSort.mergeAt(Unknown Source)
    at java.util.TimSort.mergeCollapse(Unknown Source)
    at java.util.TimSort.sort(Unknown Source)
    at java.util.Arrays.sort(Unknown Source)
    at java.util.ArrayList.sort(Unknown Source)
    at 

If you have suggestions on how to solve it, please share it with me. Thank you.

Upvotes: 0

Views: 8820

Answers (2)

JB Nizet
JB Nizet

Reputation: 691913

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.

Upvotes: 1

Andreas
Andreas

Reputation: 159135

The javadoc of compare(T o1, T o2) says:

the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.

Your implementation fails to meet that part of the contract.

If you have x = null, y = 1, z = 2, your implementation of "null equals anything", means:

compare(x, y) == 0   // Precondition met
//    0

sgn(compare(x, z)) == sgn(compare(y, z))   // Contract violated!
//      0                   -1

Upvotes: 1

Related Questions