.compareTo()有2个排序列

时间:2012-07-15 21:07:56

标签: java sorting collections comparable

我正在尝试在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或不合适的类,则需要NPE / ClassCastExceptions - 这不是问题所在。

附注2:我知道JDK 1.7中排序算法中的change,但我真的没有看到我在这里违反合同的地方。因此,关闭异常似乎是错误的解决方案。

2 个答案:

答案 0 :(得分:5)

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

Java error: "Comparison method violates its general contract!"

也许:

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被认为大于任何其他数字)。

请注意,您可以==使用int,但double使Double.NaN != Double.NaN更复杂。new Double(Double.NaN).equals(Double.NaN)。但是,x = NaN是正确的。请参阅Why is Java's Double.compare(double, double) implemented the way it is?进行讨论。

合同中断:

如果您有NaN,请查看原始实施可能违反合同的原因的示例,请参阅Java compareTo documentation。我们有:

  

最后,实现者必须确保x.compareTo(y)== 0暗示   对于所有z,sgn(x.compareTo(z))== sgn(y.compareTo(z))。

假设你有y = 5z = 6以及x.compareTo(y) == 0,那么:

  1. NaN > 5(因为NaN < 5x.compareTo(z) == 0都是假的)
  2. y.compareTo(z) == -1(同样的推理)
  3. sgn(y&lt; z)。
  4. 所以2和3(+ {{1}})根据需要不相等。

答案 1 :(得分:2)

请勿在{{1​​}}方法中返回diff。这对所有值都无效。例如,compareTo(...)的结果是否定的。

将其重写为:

Integer.MAX_VALUE - Integer.MIN_VALUE