按(x,y)坐标对偏移进行排序会导致违反比较器合同

时间:2015-12-18 17:47:55

标签: java sorting collections coordinate-systems

我试图对x和y点的集合进行排序,其中x的偏移使得p.x-q.x应该被视为等于IF p.x == q.x OR ABS(p.x-q.x)<偏移。 请注意,我在这里取绝对值,所以我不应该得到任何负值。我的比较器如下:

 private static class XYOrder implements Comparator<ExtractPojo> {

    public int compare(ExtractPojo p, ExtractPojo q) {

        Integer a=p.x.intValue();
        Integer b=q.x.intValue();
        Integer c=p.y.intValue();
        Integer d=q.y.intValue();

        int offset=Math.abs(a-b); //calculate offset from x


      if(offset < 15 || a == b) //if x is the same, sort by y coordinate
            return c-d; 
       else return a-b; //if x is not same sort by x coordinate

    }
}

此代码适用于某些情况,但我在其他情况下收到以下错误消息:

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:868)
at java.util.TimSort.mergeAt(TimSort.java:485)
at java.util.TimSort.mergeForceCollapse(TimSort.java:426)
at java.util.TimSort.sort(TimSort.java:223)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)

我有一种感觉,我违反了一个跨性别规则,因为我已经在这个网站上广泛搜索了答案,但我不确定如何测试我错过了什么这里。

我已经测试了没有偏移量的代码,但我没有收到此错误消息,所以我知道这与我实现偏移的方式有关。我已经试图解决这个问题几周而没有运气。

我希望得到一些关于此的提示或建议。非常感谢你。

所以我能想到的唯一解决方法是首先按x位置排序,然后遍历每个x并根据偏移量调整x&s,然后再按x,y进行排序上面提到没有偏移。看起来像是一个黑客,但我以前从未使用过比较器,而且我很难解决这个问题。

4 个答案:

答案 0 :(得分:5)

  

我有一种感觉,我违反了过渡性规则

确实你是。在你的比较器下,我们有(0, 20) > (10, 10) > (20, 0) > (0, 20)。 (另外,它拼写了&#34;及物性&#34;。)

通常,comparecompareToequals方法不应使用公差,因为公差本质上是不敏感的。两个差异都低于容差,可能会超出容差。

答案 1 :(得分:0)

再次检查您的代码。这就是Java文档关于compare()方法的实现所要说的。

  

当且仅当c.compare(e1,e2)== 0具有与e1.equals(e2)相同的布尔值时,比较器c对一组元素S施加的排序被认为与equals一致。对于S中的每个e1和e2。

您是否覆盖了equals()课程的ExtractPojo方法?您可以找到文档here

此外,您不应该使用a == b之类的代码。在这里,您有Integer类型的变量。因此,它只会检查2个变量是否指向同一个对象(在这种情况下可能为false。更多关于here)。请改用a.equals(b)。

答案 2 :(得分:0)

所以我做了一些测试,我相信找到了你的问题:溢出

我使你的比较方法变得非常简单,我仍然遇到了这个错误:

public int compare(ExtractPojo p, ExtractPojo q) {
    Integer x1 = p.x.intValue();
    Integer x2 = q.x.intValue();
    return x1 - x2;
}

这是因为在某些情况下x1 - x2会导致溢出违反传递性。要检查这一点,请执行以下操作:

public int compare(ExtractPojo p, ExtractPojo q) {
    Integer x1 = p.x.intValue();
    Integer x2 = q.x.intValue();
    checkSubtractionOverflow(x1, x2);
    return x1 - x2; 
}

//This will throw an exception if there is an overflow  
public static void checkSubtractionOverflow(int left, int right) {
    Math.subtractExact(left, right);
}

如果您看到异常:

Exception in thread "main" java.lang.ArithmeticException: integer overflow
at java.lang.Math.subtractExact(Unknown Source)

然后你知道你有溢出问题需要解决。

答案 3 :(得分:0)

基本上,您的代码是这样说的:

  • 如果x坐标彼此相差15个单位,请使用y坐标进行比较。
  • 如果x坐标彼此相差15个或更多,请使用x坐标进行比较
  • x坐标相等可以在不影响手头问题的情况下到达任何一方。

我怀疑传染性问题会受到影响,例如:

  • P.x在Q.x的15个单位内,P <1。 Q(因为P.y&lt; Q.y)
  • Q.x在R.x的15个单位内且Q <1。 R(因为Q.y&lt; R.y)
  • AND P.x超出R.x和P的15个单位> R(因为P.x> R.x)

此外,您正在使用.intValue()来电进行一些不必要的装箱/拆箱。 Integer a = P.x是Integer to Integer赋值。