Can someone explain me in simple terms, why does this code throw an exception, "Comparison method violates its general contract!", and how do I fix it?
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
I've seen this happen in a piece of code where the often recurring check for null values was performed:
In my case I was doing something like the following:
What I forgot to check was when both a.someField and b.someField are null.
The violation of the contract often means that the comparator is not providing the correct or consistent value when comparing objects. For example, you might want to perform a string compare and force empty strings to sort to the end with:
But this overlooks the case where BOTH one and two are empty - and in that case, the wrong value is returned (1 instead of 0 to show a match), and the comparator reports that as a violation. It should have been written as:
Your comparator is not transitive.
Let
A
be the parent ofB
, andB
be the parent ofC
. SinceA > B
andB > C
, then it must be the case thatA > C
. However, if your comparator is invoked onA
andC
, it would return zero, meaningA == C
. This violates the contract and hence throws the exception.It's rather nice of the library to detect this and let you know, rather than behave erratically.
One way to satisfy the transitivity requirement in
compareParents()
is to traverse thegetParent()
chain instead of only looking at the immediate ancestor.