比较方法违反其一般契约 - 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 -1
和 if (indexLeft >= 0 && indexRight < 0) return +1;
,以遵守规则。这就留下了一个问题:如果两者都不在里面怎么办。您可以选择没有办法订购它们(Return 0),但要知道这意味着您不能将此类项目多次放入TreeMap
或 TreeSet
- 但排序列表,没问题。单独的不可比较是允许的,它们最终会以任意顺序聚集在一起。这并没有违反规则。
我已经使用排序包装器定义了一个比较器。你能解释一下为什么这段代码会抛出异常,“比较方法违反了它的一般契约!”?如果您能告诉我如何修复它,我将不胜感激。
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 -1
和 if (indexLeft >= 0 && indexRight < 0) return +1;
,以遵守规则。这就留下了一个问题:如果两者都不在里面怎么办。您可以选择没有办法订购它们(Return 0),但要知道这意味着您不能将此类项目多次放入TreeMap
或 TreeSet
- 但排序列表,没问题。单独的不可比较是允许的,它们最终会以任意顺序聚集在一起。这并没有违反规则。