38

I know this has been an issue for a while now, and checked all previously answers I could get, but still this one doesn't work.

The object 'crew' represents crewmembers with ranks and other items. The comparison should be made by comparing 'assigned_rank', an int value, and if this value is equal in both instances, then 'is_trainer', a boolean, should make the difference.

This method worked great as long as it was running with java < 7. But since Java 7 I keep getting this one:

java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:714)
at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:451)
at java.util.ComparableTimSort.mergeCollapse(ComparableTimSort.java:376)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:182)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at java.util.Collections.sort(Collections.java:155)
at dormas_flightlog.Query.getCrew(Query.java:714)

Here is the source, where some potentially dangerous parts have allready been out-commented, but it still does not work:

public class crew implements Serializable, Comparable<crew> {

private static final long serialVersionUID = 36L;
private int flightID = 0;
private int assigned_rank = 25;
private boolean is_trainer = false;
...


@Override
public int compareTo(crew him) {

    int myRank = this.getAssigned_rank();
    int hisRank = him.assigned_rank;

    if (this == him) {
        return 0;
    }
    if (myRank > hisRank) {
        return 1;
    }
    if (myRank < hisRank) {
        return -1;
    }
    if (myRank == hisRank) {
//            if (is_trainer && !o.is_trainer) {
//                i = 1;
//            }
//            if (!is_trainer && o.is_trainer) {
//                i = -1;
//            }
//            if (is_trainer && o.is_trainer) {
//                i = 0;
//            }
//            if (!is_trainer && !o.is_trainer) {
//                i = 0;
//            }
        return 0;
    }

    return 0;
}

@Override
public int hashCode() {
    int hash = 7;
    hash = 31 * hash + this.assigned_rank;
    hash = 31 * hash + (this.is_trainer ? 1 : 0);
    return hash;
}

@Override
public boolean equals(Object o) {

    if (this == o) {
        return true;
    }


    int myRank = this.getAssigned_rank();
    int hisRank = 0;

    if (o instanceof crew) {
        crew him = (crew) o;
        hisRank = him.assigned_rank;
    } else {
        return false;
    }

    if (myRank > hisRank) {
        return false;
    }
    if (myRank < hisRank) {
        return false;
    }
    if (myRank == hisRank) {
//            if (is_trainer && !o.is_trainer) {
//                i = 1;
//            }
//            if (!is_trainer && o.is_trainer) {
//                i = -1;
//            }
//            if (is_trainer && o.is_trainer) {
//                i = 0;
//            }
//            if (!is_trainer && !o.is_trainer) {
//                i = 0;
//            }
        return true;
    }

    return false;
}

}

Implementing equals() was just a try to solve this problem. The given exception comes with or without equals(). I cannot see how the compareTo-method violates its contract. Any help is greatly appreciated....one day this code has to work with java 7 and I don't know how... Thanks

Marek Sebera
  • 39,650
  • 37
  • 158
  • 244
user1007068
  • 381
  • 1
  • 3
  • 3
  • Can you show the code where you're doing the sort ? – WilQu Oct 21 '11 at 13:06
  • And what's the difference between assigned_rank and getAssigned_rank()? – Raymond Chen Oct 21 '11 at 13:10
  • similar question asked [here](http://stackoverflow.com/questions/6626437/why-does-my-compare-method-throw-exception-comparison-method-violates-its-gen) – naresh Dec 07 '11 at 06:53
  • Did you ever find a real solution to this (besides suppressing the warning like naresh suggests)? I am having the same problem with similar code in my case. I am starting to think it is a bug in the library code. – fishinear Jan 19 '13 at 19:26
  • 1
    Your code looks good. Do you have some example objects (or preferably a whole minimal example) where this occurs? – Paŭlo Ebermann Apr 11 '13 at 19:51

4 Answers4

48

see this:

From http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#source

Area: API: Utilities Synopsis: Updated sort behavior for Arrays and Collections may throw an IllegalArgumentException

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

Nature of Incompatibility: behavioral

RFE: 6804124

For more detailed info, see the bug database reference here.

Sandeep Yohans
  • 929
  • 1
  • 14
  • 36
naresh
  • 2,113
  • 20
  • 32
  • 6
    Right, but just how does the compareTo implementation violate the contract? – Dr. Hans-Peter Störr Jul 25 '14 at 10:20
  • @hstoerr see http://stackoverflow.com/a/6626568/404165 and http://stackoverflow.com/a/6626470/404165 – naresh Nov 16 '15 at 05:12
  • It would be very helpful if you give an example for which the OP's code violates the required irreflexivity, transitivity and whatnot as required by Comparable. The linked documentation just says there must be a bug in it. Maybe I'm a little stupid here, but I just don't see it. – Dr. Hans-Peter Störr Nov 17 '15 at 10:20
10

maybe you just have NaN values which you compare through Collections.sort(...), this has been a problem to me and I got that exception even having right implementation of compare(obj1, obj2) method! Check that!

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
4

I was able to solve this error cause it was a bug in jdk7.

here I found the solution:

"Comparison method violates its general contract!" - TimSort and GridLayout

Basically i just had to add the

JAVA_OPTS="$JAVA_OPTS -Djava.util.Arrays.useLegacyMergeSort=true"

to my jboss

Community
  • 1
  • 1
cabaji99
  • 1,305
  • 11
  • 9
  • 9
    This is not a bug in jdk7, but a bug in your code. ;-) It violates the required conditions for the comparison methods. – Dr. Hans-Peter Störr Nov 17 '15 at 10:19
  • downvoting for mentioning that the bug is in jdk7 without giving any explaination. – Archit Jan 15 '19 at 04:37
  • 1
    lol your comments gave me good laugh. I down voted as well. The useLegacyMegaSort is a temporary workaround (not a solution) meant for backward compatibility for Java < 7. – MG Developer May 07 '19 at 20:39
0

Unfortunately, none of the solutions work for Android. TimSort is used deep in Android's ViewGroup relating to addChildrenForAccessibility that shows up under Java 7 & 8. No user code is involved in any comparison.

From other reports, it is related to having RelativeLayout with overlapping items as is commonly done. For example, a TextView that appears over an Image, or two items at the same location, where you only set one visible at a time. https://code.google.com/p/android/issues/detail?id=55933

I've not found any way around the bug. You can't set a -Djava option in Android Studio or Eclipse (at least that I could find). Forcing use of Java 1.6 should work, but doesn't. Seems like Amazon's newer Fire tablets and phones are far more sensitive to this bug than other devices.

There are rumors Java 9 will have a fix such as a run-time option that works, but with a bug that's been around for years, I have doubts it will ever be fixed - especially considering the animosity between Oracle and Google. Any yes, perhaps the bug is really deep in the Android code and should be fixed there. With more than a billion devices out there, that's not a viable solution for all the existing devices.

Frank
  • 605
  • 1
  • 8
  • 14