java.lang.IllegalArgumentException: Comparison method violates its general contract [duplicate]
Hi below is my compare method of my comparator. I am not sure what is wrong. I looked up other similar titled questions and answers on stack overflow but not sure what is wrong with my method but I keep getting java.lang.IllegalArgumentException: Comparison method violates its general contract!
Any help will be greatly appreciated
public int compare(Node o1, Node o2)
{
HashMap<Integer,Integer> childMap = orderMap.get(parentID);
if(childMap != null && childMap.containsKey(o1.getID()) &&
childMap.containsKey(o2.getID()))
{
int order1 = childMap.get(o1.getID());
int order2 = childMap.get(o2.getID());
if(order1<order2)
return -1;
else if(order1>order2)
return 1;
else
return 0;
}
else
return 0;
}
Adding the exception I am getting
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:747)
at java.util.TimSort.mergeAt(TimSort.java:483)
at java.util.TimSort.mergeCollapse(TimSort.java:410)
at java.util.TimSort.sort(TimSort.java:214)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
Solution 1:
Your compare()
method is not transitive. If A == B
and B == C
, then A
must be equal to C
.
Now consider this case:
For A
, B
, and C
, suppose the containsKey()
method return these results:
-
childMap.containsKey(A.getID())
returnstrue
-
childMap.containsKey(B.getID())
returnsfalse
-
childMap.containsKey(C.getID())
returnstrue
Also, consider orders for A.getId()
!= B.getId()
.
So,
-
A
andB
would return0
, as outerif
condition will befalse
=>A == B
-
B
andC
would return0
, as outerif
condition will befalse
=>B == C
But, A
and C
, could return -1
, or 1
, based on your test inside the if
block. So, A != C
. This violates the transitivity principle.
I think, you should add some condition inside your else
block, that performs check similar to how you do in if
block.
Solution 2:
I think the problem is in your default case. Consider the set of nodes A, B, and C, where the IDs are 'a'
, 'b'
, and 'c'
. Consider further that your childMap
, which contains the relative ordering information, has the following contents:
{ 'a' => 1, 'c' => 3 }
Now if you run your compare
method on A and B, you return 0
, indicating that A and B are equivalent. Further, if you compare B and C, you still return 0
. However, if you compare A and C, then you return -1
, indicating that A is smaller. This violates the transitivity property of the Comparator
contract:
The implementor must also ensure that the relation is transitive:
((compare(x, y)>0) && (compare(y, z)>0))
impliescompare(x, z)>0
.Finally, the implementor must ensure that
compare(x, y)==0
implies thatsgn(compare(x, z))==sgn(compare(y, z))
for allz
.
You can't treat "items which don't have an order assigned" as having the value "somewhere vaguely in the middle", since sorting algorithms don't know where to put them. If you want to stay with this approach, then in the case where the value is not present in the map, you need to assign a fixed value to be the ordering number; something like either 0
or MIN_INT
is a reasonable choice (but any choice needs to be documented in the Javadoc for compare
!).