Search code examples
javacollectionscomparable

Java 7 - "Comparison method violates its general contract!"


Everything seemed to be running okay (for several days), but I ran into an issue only once and having a really hard time to reproduce the problem.

"Comparison method violates its general contract!" was thrown and completely caught me off guard. I have the following:

public class CustomComparator implements Comparator<Chromosome> {

public int compare(Chromosome c1, Chromosome c2){

    return c1.compareTo(c2);
}

} 

My Chromosome class:

public class Chromosome implements Comparable<Chromosome>{

private double rank;

//bunch of methods...

@Override public int compareTo(Chromosome c){

    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if (this.getRank() == c.getRank()) //getRank() simply returns a double value 'rank'
        return EQUAL;

    else if (this.getRank() < c.getRank())
            return BEFORE;

    else  //i.e. (this.getRank() > c.getRank())
        return AFTER;   

}

I have an ArrayList and I used both Collections.sort(MyList) and Collections.sort(MyList, Collections.reverseOrder()). They're still working fine up till now. I just ran into that error only once out of 100's of run. Is there something wrong with this implementation?


Solution

  • Java 7 has changed the behavior of their sorting algorithms a bit. They now throw an Exception if a violation of the general contract for the compareTo method is detected. You can read about that contract's properties for example here.

    In general it could be violated for example in case that it would resolve to a < b and b < a. If this was detected before Java 7, it was just silently ignored. Now an Exception will be thrown.

    If you want to use the old behaviour, you may use the following:

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

    But I don't encourage you to do this. You should just change your implementation to the standard implementation of double comparison via Double.compare(a, b). This implementation correctly deals with infinity and NaN values for doubles.

    Furthermore if your Comparator just delegates to the compareTo method, it can be discarded in general.