【问题标题】:Comparison method violates its general contract java7比较方法违反了它的一般合同java7
【发布时间】:2015-10-10 17:14:33
【问题描述】:

我知道很多答案已经回答了我的问题。在我的代码中,异常说“比较方法违反了其一般合同”,但我不知道我的比较方法如何违反其一般合同。这是我的代码:

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

【问题讨论】:

    标签: java comparison comparator


    【解决方案1】:

    这里发生了一些微妙的事情。这不是 Stack Overflow 上其他地方常见的“比较器损坏”问题。虽然这个比较器确实坏了,但很难看到。

    第一个问题是比较器本质上是负责比较Double的值,也就是装箱的double值。 &gt; 运算符将执行自动拆箱并对包含的值进行数字比较,而 == 运算符将测试引用相等性。一般来说,

    Double.valueOf(1.23) == Double.valueOf(1.23) // WARNING: reference comparison, not numeric!
    

    将是false。如果你真的想测试 Double 值的数值相等性,你必须这样做

    if (o1.getValue().doubleValue() == o2.getValue.doubleValue()) ...
    

    如果您的输入仅包含实际数值,这将主要工作。不过,我怀疑您的输入包含 NaN 值,这些值具有晦涩(甚至是荒谬)的行为。特别是,将NaN 与任何数值进行比较是错误的,而NaN 与自身进行比较不等于!这违反了有关数字比较的各种规则;实际上,NaN 相对于实数是无序的。这就是排序算法在遇到NaN 值时中断的原因。

    NaN 是 0.0 除以 0.0 的结果。)

    有一个方法Double.compare(double d1, double d2)可以合理的处理NaN;它对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 上使用实用方法来避免编写自己的比较器。您也可以在List 上使用sort() 默认方法,这通常会更快:

    if (isDesc) {
        res.sort(Map.Entry.<Integer,Double>comparingByValue().reversed());
    } else {
        res.sort(Map.Entry.comparingByValue());
    }
    

    (不幸的是,类型推断不太有效,因此您必须提供“类型见证”才能获得反向比较器。)

    最后,您可以使用“菱形”运算符和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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-15
      相关资源
      最近更新 更多