我知道很多答案都回答了我的问题。在我的代码中,例外说"比较方法违反了它的一般合同",但我不知道我的比较方法如何违反其一般合同。这是我的代码:
public static List<Entry<Integer, Double>> sortMap(
Map<Integer, Double> curMap, final boolean isDesc) {
List<Entry<Integer, Double>> res = new ArrayList<Entry<Integer, Double>>();
for (Entry<Integer, Double> iter : curMap.entrySet()) {
res.add(iter);
}
Collections.sort(res, new Comparator<Entry<Integer, Double>>() {
public int compare(Entry<Integer, Double> o1,
Entry<Integer, Double> o2) {
if (o1.getValue() == o2.getValue()) {
return 0;
} else if (o1.getValue() > o2.getValue()) {
return isDesc ? -1 : 1;
}
return isDesc ? 1 : -1;
}
});
return res;
}
答案 0 :(得分:2)
这里有一些微妙的事情。这不是通常的&#34;破坏的比较器&#34; Stack Overflow上其他地方看到的问题。虽然这个比较器确实被打破了,但很难看到。
第一个问题是比较器基本上负责比较Double
值,即盒装double
值。 >
运算符将执行自动拆箱并对包含的值进行数值比较,而==
运算符将测试引用相等性。一般来说,
Double.valueOf(1.23) == Double.valueOf(1.23) // WARNING: reference comparison, not numeric!
将是false
。如果您真的想测试Double
值的数字相等性,则必须执行
if (o1.getValue().doubleValue() == o2.getValue.doubleValue()) ...
如果您的输入仅包含实际数值,这将主要起作用。但我怀疑,您的输入包含NaN
值,这些值具有模糊(甚至无意义)的行为。特别是,将NaN
与任何数值进行比较均为false,NaN
与自身的比较不等于!这违反了有关数字比较的各种规则;实际上,NaN
与实数无关。这就是排序算法在遇到NaN
值时中断的原因。
(NaN
是将0.0除以0.0的结果。)
有一种合理处理NaN
的方法Double.compare(double d1, double d2)
;它会将NaN
值排除在Double.POSITIVE_INFINITY
之上。 (它还将正零和负零区分开来,但这不太可能导致您的问题。)有一种配套方法Double.compareTo(Double)
可以比较加框的Double
值。
我会像这样重写你的比较器:
Collections.sort(res, new Comparator<Entry<Integer, Double>>() {
public int compare(Entry<Integer, Double> o1,
Entry<Integer, Double> o2) {
if (isDesc) {
return o2.getValue().compareTo(o1);
} else {
return o1.getValue().compareTo(o2);
}
}
}
由于Double
本身是Comparable
,因此在Java 8中,您可以避免使用Map.Entry
上的实用程序方法编写自己的比较器。您还可以使用sort()
上的List
默认方法,这种方法通常会更快:
if (isDesc) {
res.sort(Map.Entry.<Integer,Double>comparingByValue().reversed());
} else {
res.sort(Map.Entry.comparingByValue());
}
(不幸的是,类型推断并不是很有效,所以你必须提供一个&#34;类型见证&#34;以获得反向比较器。)
最后,您可以使用&#34;钻石&#34;运算符和ArrayList
复制构造函数可以更加简洁地复制映射条目。重写的例程如下所示:
public static List<Entry<Integer, Double>> sortMap(
Map<Integer, Double> curMap, final boolean isDesc) {
List<Entry<Integer, Double>> res = new ArrayList<>(curMap.entrySet());
if (isDesc) {
res.sort(Map.Entry.<Integer,Double>comparingByValue().reversed());
} else {
res.sort(Map.Entry.comparingByValue());
}
return res;
}