下面是我比较器的比较方法。我不确定是什么问题。我在堆栈溢出时查找了其他类似的标题问题和答案,但不确定我的方法有什么问题,但我一直得到java.lang.IllegalArgumentException:比较方法违反了它的一般合同!
非常感谢任何帮助
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;
}
添加我得到的例外
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)
答案 0 :(得分:57)
您的compare()
方法不传递 。如果A == B
和B == C
,则A
必须等于C
。
现在考虑一下这个案例:
对于A
,B
和C
,假设containsKey()
方法会返回以下结果:
childMap.containsKey(A.getID())
返回true
childMap.containsKey(B.getID())
返回false
childMap.containsKey(C.getID())
返回true
另外,请考虑A.getId()
!= B.getId()
的订单。
所以,
A
和B
将返回0
,因为外if
条件将为false
=&gt; A == B
B
和C
将返回0
,因为外if
条件将为false
=&gt; B == C
但是,A
和C
可以根据-1
块内的测试返回1
或if
。所以,A != C
。这违反了传递性原则。
我认为,您应该在else
块中添加一些条件,执行与if
块中的操作类似的检查。
答案 1 :(得分:3)
我认为问题出在你的默认情况下。考虑节点A,B和C的集合,其中ID为'a'
,'b'
和'c'
。进一步考虑包含相关订购信息的childMap
具有以下内容:
{ 'a' => 1, 'c' => 3 }
现在,如果您在A和B上运行compare
方法,则返回0
,表示A和B是等效的。此外,如果您比较B和C,您仍然会返回0
。但是,如果比较A和C,则返回-1
,表示A较小。这违反了the Comparator
contract的传递属性:
实现者还必须确保关系具有传递性:
((compare(x, y)>0) && (compare(y, z)>0))
隐含compare(x, z)>0
。最后,实施者必须确保
compare(x, y)==0
暗示所有sgn(compare(x, z))==sgn(compare(y, z))
都z
。
由于排序算法不知道将它们放在何处,因此您不能将“未分配订单的项目”视为具有“隐藏在中间某处”的值。如果您希望继续使用此方法,那么在地图中不存在该值的情况下,您需要将固定值指定为订购号;像0
或MIN_INT
之类的东西是一个合理的选择(但任何选择都需要记录在compare
的Javadoc中。)。