2

我正在尝试在 Java 中为需要按两个不同列/变量排序的对象实现可比较的接口。我尝试了多种方法,这是迄今为止最好的一种:

public int compareTo(Object o) {
    Match m = (Match)o;
    int diff = m.matches - matches;
    if (diff == 0) {
        if (distance > m.distance) {
            return 1;
        } else if (distance < m.distance) {
            return -1;
        } else {
            return 0;
        }
    } else {
        return diff;
    }
}

但它仍然失败

java.lang.IllegalArgumentException: Comparison method violates its general contract!

任何想法我做错了什么?

旁注 1:如果 o 为 null 或属于不合适的类,则预期会出现 NPEs/ClassCastExceptions - 这不是这里的问题。

旁注 2:我知道 JDK 1.7 中排序算法的变化,但我真的不知道我在哪里违反了合同。所以关闭异常似乎是错误的解决方案。

4

2 回答 2

5

由于您说distance的是双倍,因此您可能遇到与此处所述相同的问题:

Java 错误:“比较方法违反了它的一般约定!”

也许:

public int compareTo(Object o) {
    Match m = (Match)o;
    int diff = m.matches - matches;
    if (diff == 0) {
        return Double.compare(distance, m.distance);
    } else {
        return diff;
    }
}

但是,理想情况下,您应该使用我在下面说明的内置比较方法。上面的代码是“需要的最小更改”的示例,说明了关键问题。

使用现有的比较方法

此外,正如@fabian-barney 在他的回答中所说,您应该避免采用直接差异,而是使用内置的比较方法。所以你应该有类似的东西:

public int compareTo(Object o) {
    Match m = (Match) o;
    return m.matches == matches ? Double.compare(m.distance, distance) : Integer.compare(m.matches, matches);
}

这样,Double.compare将为您处理 NaN 值。对于任何数字x(NaN 除外)Double.compare(x, Double.NaN) == -1都将返回 true(即,NaN 被认为大于任何其他数字)。

请注意,您可以使用==with ints 但它更复杂double因为Double.NaN != Double.NaN。然而,new Double(Double.NaN).equals(Double.NaN)确实如此。请参阅为什么 Java 的 Double.compare(double, double) 以这种方式实现?进行愉快的讨论。

违约:

要查看一个示例,说明如果您有 NaN,您的原始实现可能会违反合同,请参阅Java compareTo 文档。我们有:

最后,实施者必须确保对于所有 z,x.compareTo(y)==0 意味着 sgn(x.compareTo(z)) == sgn(y.compareTo(z))。

所以想象你有x = NaNand y = 5z = 6然后:

  1. x.compareTo(y) == 0(因为NaN > 5NaN < 5是假的)
  2. x.compareTo(z) == 0(同理)
  3. y.compareTo(z) == -1(y < z)。

所以 2 和 3 (+ sgn) 不等于所要求的。

于 2012-07-15T21:33:15.293 回答
2

不要返回diffincompareTo(...)方法。这不适用于所有值。例如,结果Integer.MAX_VALUE - Integer.MIN_VALUE是否定的。

将其重写为:

public int compareTo(Object o) {
    Match m = (Match) o;
    return m.matches == matches ? Double.compare(m.distance, distance) : Integer.compare(m.matches, matches);
}
于 2012-07-15T21:51:27.543 回答