比较方法违反了其一般合同java7

时间:2015-07-21 09:57:23

标签: java comparison comparator

我知道很多答案都回答了我的问题。在我的代码中,例外说"比较方法违反了它的一般合同",但我不知道我的比较方法如何违反其一般合同。这是我的代码:

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;
}

1 个答案:

答案 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;
}