我正在尝试在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,但我真的没有看到我在这里违反合同的地方。因此,关闭异常似乎是错误的解决方案。
答案 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 = 5
和z = 6
以及x.compareTo(y) == 0
,那么:
NaN > 5
(因为NaN < 5
和x.compareTo(z) == 0
都是假的)y.compareTo(z) == -1
(同样的推理)sgn
(y&lt; z)。所以2和3(+ {{1}})根据需要不相等。
答案 1 :(得分:2)
请勿在{{1}}方法中返回diff
。这对所有值都无效。例如,compareTo(...)
的结果是否定的。
将其重写为:
Integer.MAX_VALUE - Integer.MIN_VALUE