I am trying to implement the comparable interface in Java for an object that I need to sort by two different columns/variables. I tried multiple approaches, and this one is the best so far:
public int compareTo(Object o) {
Match m = (Match)o;
int diff = m.matches - matches;
if (diff == 0) {
if (distance > m.distance) {
return 1;
} else if (distance < m.distance) {
return -1;
} else {
return 0;
}
} else {
return diff;
}
}
but it still fails with
java.lang.IllegalArgumentException: Comparison method violates its general contract!
Any ideas what I’m doing wrong?
Side note 1: NPEs/ClassCastExceptions are expected, if o is null or of an unsuitable class – that is not the issue here.
Side note 2: I know about the change in the sorting algorithm in JDK 1.7, but I don’t really see where I’m violating the contract here. So turning off the exception seems like the wrong solution.
Since you say
distanceis a double, you likely have the same problem as described here:Java error: "Comparison method violates its general contract!"
Perhaps:
However ideally you should use the built-in comparison methods as I state below.
The above code is an example of the “minimum change needed”, illustrating the key problem.
Using existing comparison methods
Also, as @fabian-barney states in his answer, you should avoid taking the direct difference, and instead utilise the built-in comparison methods. So you should have something like:
This way,
Double.comparewill handle the NaN values for you. For any numberx(other than NaN)Double.compare(x, Double.NaN) == -1will return true (i.e., NaN is considered greater than any other number).Note here that you are OK using
==withints but it is more complicated withdoublebecauseDouble.NaN != Double.NaN. However,new Double(Double.NaN).equals(Double.NaN)is true. See Why is Java's Double.compare(double, double) implemented the way it is? for a nice discussion.Contract breaking:
To see an example of why your original implementation might break the contract if you have NaNs, see Java compareTo documentation. There we have:
So imagine you have
x = NaNandy = 5andz = 6, then:x.compareTo(y) == 0(sinceNaN > 5andNaN < 5are false)x.compareTo(z) == 0(same reasoning)y.compareTo(z) == -1(y < z).So 2 and 3 (+
sgn) are not equal as required.