比较方法违反其一般契约 - Java 错误

Comparison method violates its general contract - Java Error

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

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

    }   

这是合同:

  • 如果`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,你的代码总是 returns 1,这违反了规则(a.compare(b),其中 a 不在地图中,returns 1,而 b.compare(a) 也会 return 1 . 它需要 return 一个负数来代替)。

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

如果 的意图,您将不得不制定一些规则。例如:如果 a 在映射中但 b 不在映射中,则 a 始终高于 b。这意味着 if (indexLeft < 0 && indexRight >= 0) return -1if (indexLeft >= 0 && indexRight < 0) return +1;,以遵守规则。这就留下了一个问题:如果两者都不在里面怎么办。您可以选择没有办法订购它们(Return 0),但要知道这意味着您不能将此类项目多次放入TreeMapTreeSet - 但排序列表,没问题。单独的不可比较是允许的,它们最终会以任意顺序聚集在一起。这并没有违反规则。