Tanu Garg
Tanu Garg

Reputation: 3067

Getting error: Comparison method violates its general contract

I have tried many possible solution given on the net like to set System property and to convert in double but still getting same error:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:835)
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:453)
    at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:392)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:191)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
    at java.util.Arrays.sort(Arrays.java:472)
    at java.util.Collections.sort(Collections.java:155)

Here is my code:

        System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
        Collections.sort(docs, new Comparator<FeedDocument>() {
            public int compare(FeedDocument o1, FeedDocument o2) {

                int year1 = 0;
                int year2 = 0;
                int returnResult = 0;
                if (o1.containsKey(FeedConstants.PUBLICATION_YEAR)
                        && o2.containsKey(FeedConstants.PUBLICATION_YEAR)
                        && o1.get(FeedConstants.PUBLICATION_YEAR) != null
                        && (o1.get(FeedConstants.PUBLICATION_YEAR) instanceof String)
                        && o2.get(FeedConstants.PUBLICATION_YEAR) != null
                        && (o2.get(FeedConstants.PUBLICATION_YEAR) instanceof String)) {

                    String firstyear = (String) o1.get((FeedConstants.PUBLICATION_YEAR));
                    String secondyear = (String) o2.get((FeedConstants.PUBLICATION_YEAR));

                    if (firstyear.equals(secondyear)) {
                        return 0;
                    } else if (firstyear != null && !firstyear.isEmpty() && secondyear != null
                            && !secondyear.isEmpty()) {

                        year1 = Integer.parseInt(firstyear.trim());

                        year2 = Integer.parseInt(secondyear.trim());

                        // int result = year2 - year1;
                        // if (result > 0) {
                        // returnResult = 1;
                        // } else if (result < 0) {
                        // returnResult = -1;
                        // }
                        return Double.compare(year2, year1);
                    }

                } else {
                    returnResult = 0;
                }
                return returnResult;
            }
        });

Upvotes: 1

Views: 1918

Answers (2)

mapeters
mapeters

Reputation: 1117

Edward Peters' answer is correct in diagnosing your problem, as your compare method doesn't produce consistent (transitive) results.

The best way to resolve this is something like the following in your compare method:

if (o1.get(FeedConstants.PUBLICATION_YEAR) instanceof String) {
    if (o2.get(FeedConstants.PUBLICATION_YEAR) instanceof String) {
        // Perform the comparison here like you are
    } else {
        /*
         * This could also be 1, the key is to have it consistent
         * so the final sorted list clearly separates the FeedDocuments
         * with a String PUBLICATION_YEAR and those without one.
         */
        return -1;
    }
} else if (o2.get(FeedConstants.PUBLICATION_YEAR) instanceof String) {
    /*
     * In this case, o1 doesn't have a String PUBLICATION_YEAR and o2
     * does, so this needs to be the opposite of the return value
     * 6 lines up to be consistent.
     */
     return 1;
} else {
     /*
      * Consider all FeedDocuments without a String PUBLICATION_YEAR
      * to be equivalent, otherwise you could do some other comparison
      * on them here if you wanted.
      */
     return 0;
}

The key is, if you only care about a subset of the list being sorted (the FeedDocuments with a String publication year, then you need to first separate them from the rest of the list that you don't care about being sorted (by returning either 1 or -1 when one of the FeedDocuments has a String publication year and the other doesn't). Then, you are free to sort the desired subset without inconsistent results.

Upvotes: 0

Edward Peters
Edward Peters

Reputation: 4062

Pretty sure I know what's happening here...

Suppse:

o1.get(FeedConstants.PUBLICATION_YEAR) != null
o2.get(FeedConstants.PUBLICATION_YEAR) == null
o3.get(FeedConstants.PUBLICATION_YEAR) != null

Then:

compare (o1, o2); //returns 0
compare (o2, o3); //returns 0
compare (o1, o3); //returns not 0

So you're claiming o1 == o2 == o3 but o1 != o3

Upvotes: 4

Related Questions