java.lang.IllegalArgumentException:比较方法违反了其一般合同

时间:2013-10-11 18:53:16

标签: java comparison

下面是我比较器的比较方法。我不确定是什么问题。我在堆栈溢出时查找了其他类似的标题问题和答案,但不确定我的方法有什么问题,但我一直得到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)

2 个答案:

答案 0 :(得分:57)

您的compare()方法传递 。如果A == BB == C,则A必须等于C

现在考虑一下这个案例:

对于ABC,假设containsKey()方法会返回以下结果:

  • childMap.containsKey(A.getID())返回true
  • childMap.containsKey(B.getID())返回false
  • childMap.containsKey(C.getID())返回true

另外,请考虑A.getId()!= B.getId()的订单。

所以,

  1. AB将返回0,因为外if条件将为false =&gt; A == B
  2. BC将返回0,因为外if条件将为false =&gt; B == C
  3. 但是,AC可以根据-1块内的测试返回1if。所以,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

由于排序算法不知道将它们放在何处,因此您不能将“未分配订单的项目”视为具有“隐藏在中间某处”的值。如果您希望继续使用此方法,那么在地图中不存在该值的情况下,您需要将固定值指定为订购号;像0MIN_INT之类的东西是一个合理的选择(但任何选择都需要记录在compare的Javadoc中。)。