首页 > 解决方案 > 比较方法违反了它的一般约定 - Java 错误

问题描述

我已经使用排序包装器定义了一个比较器。你能解释一下为什么这段代码会抛出一个异常,“比较方法违反了它的一般合同!”?如果您能告诉我如何解决它,我将不胜感激。

Ordering<Foo> order = new Ordering<Foo>() {

        @Override
        public int compare(Foo left, Foo right) {
                return getCompare(orderMap, left.getItemId(), right.getItemId());
        }
};

Collections.sort(Foos, order);

比较:

private int getCompare(Map<Long, Integer> orderMap, Long leftId, Long rightId) {

        int indexLeft = orderMap.get(leftId) == null ? -1 : orderMap.get(leftId);
        int indexRight = orderMap.get(leftId) == null ? -1 : orderMap.get(rightId);

        if (indexLeft < 0 || indexRight < 0) {
            return 1;
        }

        return Integer.compare(indexLeft, indexRight);

    }   

标签: javaexceptioncollectionscomparisoncomparator

解决方案


这是合同:

  • 如果 `a.compare(b) 是 X,并且 b.compare(c) 是 X,那么 a.compare(c) 也必须是 X,无论 X 是负数、正数还是零。
  • 如果a.compare(b)是 X,那么b.compare(a)必须是 -X:0 仍然是 0,-1 变成 +1,等等。
  • a.compare(a)必须为 0。

而已。您的比较方法在很多方面打破了这一点。例如,您的第二行中有一个错误(肯定是orderMap.get(rightId) == null,您可以改用它来清理它getOrDefault),如果找不到任何索引或小于 0,您的代码总是返回 1,这违反了规则 ( a.compare(b),其中 a 不在地图中,返回 1,也b.compare(a)将返回 1。它需要返回一个负数)。

如果其中一个不在地图中,您将不得不想出一个规则来处理会发生什么。如果您的代码是在假设它不会发生的情况下编写的,那么它是 - 当您的假设不成立时抛出异常,以便您可以调查为什么您的假设(所有提供的 left/rightIds 始终在地图中并且始终非负数)。正如所写的那样,如果发生这种情况,您的代码会以一种令人讨厌的方式直接上升——这就是异常的用途。以易于调试的方式展开。

如果这意图,您将不得不制定一些规则。例如:如果a在地图中但b不在地图中,那么 a 总是高于 b。这意味着if (indexLeft < 0 && indexRight >= 0) return -1而且if (indexLeft >= 0 && indexRight < 0) return +1;,为了遵守规则。这就留下了一个问题:如果两者都不在怎么办。您可以选择没有办法订购它们(返回 0),但要知道这意味着您不能在 aTreeMapTreeSet- 但排序一个清单,很好。允许单独的不可比较的,它们最终会以任意顺序聚集在一起。这并不违反规则。


推荐阅读