javasortingexceptionillegalargumentexceptiontimsort

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.


Solution

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