Java SortedSet +比较器,与 equals() 的一致性问题

发布于 2024-08-06 09:16:34 字数 261 浏览 6 评论 0原文

我想要一个集合的 SortedSet(在本例中是集合本身,但不一定是一般情况),按集合大小排序。这似乎违反了使比较器与 equals() 一致的规定 - 即,两个集合可能不相等(通过具有不同的元素),但比较相同的值(因为它们具有相同数量的元素)。

理论上,我还可以放入比较器方法来对相同大小的集合进行排序,但是排序的使用不会利用这一点,并且没有真正有用的+直观的方法来比较相同大小的集合(至少,就我的具体情况而言),所以这似乎是一种浪费。

这种不一致的情况看起来是一个问题吗?

I'd like to have a SortedSet of Collections (Sets themselves, in this case, but not necessarily in general), that sorts by Collection size. This seems to violate the proscription to have the Comparator be consistent with equals() - i.e., two collections may be unequal (by having different elements), but compare to the same value (because they have the same number of elements).

Notionally, I could also put into the Comparator ways to sort sets of equal sizes, but the use of the sort wouldn't take advantage of that, and there's not really a useful + intuitive way to compare the Collections of equal size (at least, in my particular case), so that seems like a waste.

Does this case of inconsistency seem like a problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

笔落惊风雨 2024-08-13 09:16:34

SortedSet 接口扩展核心 Set< /code>,因此应符合 Set 规范中概述的合同。

实现这一目标的唯一可能方法是让元素的 equal() 方法行为与您的 Comparator 保持一致 - 其原因是核心 Set 基于相等进行操作,而 SortedSet 基于比较进行操作。

例如, add()方法指定,如果已经存在其 equal() 方法将返回的元素,则无法将元素添加到集合中true 以这个新元素作为参数。好吧,SortedSet 不使用 equal(),它使用 compareTo()。因此,如果您的 compareTo() 返回 false,即使 equals() 返回 true,您的元素也会被添加,从而破坏了 Set 契约。

然而,这一切本身都不是一个实际问题。 SortedSet 行为始终一致,即使 compare()equals() 不一致。

SortedSet interface extends core Set and thus should conform to the contract outlined in Set specification.

The only possible way to achieve that is to have your element's equal() method behavior be consistent with your Comparator - the reason for that is that core Set operates based on equality whereas SortedSet operates based on comparison.

For example, add() method defined in core Set interface specifies that you can't add an element to the set if there already is an element whose equal() method would return true with this new element as argument. Well, SortedSet doesn't use equal(), it uses compareTo(). So if your compareTo() returns false your element WILL be added even if equals() were to return true, thus breaking the Set contract.

None of this is a practical problem per se, however. SortedSet behavior is always consistent, even if compare() vs equals() are not.

锦爱 2024-08-13 09:16:34

正如 ChssPly76 在评论中所写,在两个 Collections 大小相同但不相等的情况下,您可以使用 hashCode 来决定compareTo 调用。这工作得很好,除非在极少数情况下,您有两个大小相同、不相等但具有相同 hashCode 的集合。诚然,这种情况发生的可能性很小,但也是可以想象的。如果您想非常小心,请使用 System.identityHashCode 而不是 hashCode。这应该为每个集合提供一个唯一的编号,并且您不应该发生冲突。

最终,这为您提供了按大小对集合中的集合进行排序的功能,并且在两个具有匹配大小的集合的情况下可以任意排序。如果这就是您所需要的,那么它并不比通常的比较慢多少。如果您需要不同 JVM 实例之间的顺序保持一致,那么这将不起作用,您必须采用其他方式。

伪代码:

if (a.equals(b)) {
    return 0;
} else if (a.size() > b.size()) {
    return 1;
} else if (b.size() > a.size()) {
    return -1;
} else {
    return System.identityHashCode(a) > System.identityHashCode(b) ? 1 : -1;
}

As ChssPly76 wrote in a comment, you can use hashCode to decide the compareTo call in the case where two Collections have the same size but are not equal. This works fine, except in the rare case where you have two Collections with the same size, are not equal, but have the same hashCode. Admittedly, the chances of that happening are pretty small, but it is conceivable. If you want to be really careful, instead of hashCode, use System.identityHashCode instead. This should give you a unique number for each Collection, and you shouldn't get collisions.

At the end of the day, this gives you the functionality of having the Collections in the Set sorted by size, with arbitrary ordering in the case of two Collections with matching size. If this is all you need, it's not much slower than the usual comparison would be. If you need the ordering to be consistent between different JVM instances, this won't work and you'll have to do it some other way.

pseudocode:

if (a.equals(b)) {
    return 0;
} else if (a.size() > b.size()) {
    return 1;
} else if (b.size() > a.size()) {
    return -1;
} else {
    return System.identityHashCode(a) > System.identityHashCode(b) ? 1 : -1;
}
岁月如刀 2024-08-13 09:16:34

这似乎违反了禁令
使比较器保持一致
使用 equals() - 即两个集合
可能是不平等的(通过具有不同的
元素),但与相同的比较
值(因为它们具有相同的
元素数量)。

没有要求(在 Javadoc 中)明示或暗示,Comparator 必须与对象的 boolean equals(Object) 实现一致。

请注意,ComparableComparator 是具有不同用途的不同接口Comparable 用于定义类的“自然”顺序。在这种情况下,equalscompateTo 不一致是一个坏主意。相比之下,当您想要使用与类的自然顺序不同的顺序时,可以使用Comparator

编辑:这是来自 SortedSet 的 Javadoc 的完整段落。

请注意,排序由
排序集(无论是否显式
提供比较器)必须是
如果已排序,则与 equals 一致
set就是正确实现Set
界面。 (参见可比
接口或比较器接口
一致的精确定义
与等于。)这是因为
设置接口定义为
equals 操作,但是排序
set 执行所有元素比较
使用它的compareTo(或compare)
方法,所以两个元素是
通过这种方法认为相等的是,从
排序集的观点,
平等的。 有序集的行为是
明确定义,即使其顺序是
与等于不一致;它只是
不遵守总承包合同
设置界面。

我已经突出显示了最后一句话。关键是这样的 SortedSet 将按照您最有可能期望的方式工作,但是某些操作的行为不会完全符合 Set 规范...因为该规范根据equals 方法。

所以事实上,有一个明确的一致性要求(我的错误),但忽略它的后果并不像你想象的那么糟糕。当然,是否应该这样做取决于您自己决定。在我看来,应该没问题,只要您彻底注释代码并确保 SortedSet 不会“泄漏”。

然而,我不清楚仅查看集合“大小”的集合比较器是否会起作用......从语义角度来看。我的意思是,您真的想说所有具有(例如)2 个元素的集合都是相等的吗?这意味着您的集合只能包含任何给定大小的一个集合......

This seems to violate the proscription
to have the Comparator be consistent
with equals() - i.e., two collections
may be unequal (by having different
elements), but compare to the same
value (because they have the same
number of elements).

There is no requirement, either stated (in the Javadoc) or implied, that a Comparator be consistent with an object's implementation of boolean equals(Object).

Note that Comparable and Comparator are distinct interfaces with different purposes. Comparable is used to define a 'natural' order for a class. In that context, it would be a bad idea for equals and compateTo to be inconsistent. By contrast, a Comparator is used when you want to use a different order to the natural order of a class.

EDIT: Here's the complete paragraph from the Javadoc for SortedSet.

Note that the ordering maintained by a
sorted set (whether or not an explicit
comparator is provided) must be
consistent with equals if the sorted
set is to correctly implement the Set
interface. (See the Comparable
interface or Comparator interface for
a precise definition of consistent
with equals.) This is so because the
Set interface is defined in terms of
the equals operation, but a sorted
set performs all element comparisons
using its compareTo (or compare)
method, so two elements that are
deemed equal by this method are, from
the standpoint of the sorted set,
equal. The behavior of a sorted set is
well-defined even if its ordering is
inconsistent with equals; it just
fails to obey the general contract of
the Set interface.

I have highlighted the final sentence. The point is that such a SortedSet will work as you would most likely expect, but the behavior of some operations won't exactly match the Set specification ... because the specification defines their behavior in terms of the equals method.

So in fact, there is a stated requirement for consistency (my mistake), but the consequences of ignoring it are not as bad as you might think. Of course, it is up to decide if you should do that. In my estimation, it should be OK, provided that you comment the code thoroughly and make sure that the SortedSet does not 'leak'.

However, it is not clear to me that a Comparator for collections that only looks at an collections "size" is going to work ... from a semantic perspective. I mean, do you really want to say that all collections with (say) 2 elements are equal? This will mean that your set can only ever contain one collection of any given size ...

不必你懂 2024-08-13 09:16:34

Comparator 没有理由返回与 equals() 相同的结果。事实上,引入 Comparator API 是因为 equals() 还不够:如果要对集合进行排序,则必须知道两个元素是小于还是大于。

There is no reason why a Comparator should return the same results as equals(). In fact, the Comparator API was introduced because equals() just isn't enough: If you want to sort a collection, you must know whether two elements are lesser or greater.

戈亓 2024-08-13 09:16:34

有点奇怪的是,SortedSet 作为标准 API 的一部分,打破了 Set 接口中定义的约定,并使用 Comparator 来定义相等性而不是 equals 方法,但事实就是如此。

如果您的实际问题是根据包含的集合的大小对集合的集合进行排序,那么您最好使用 List,您可以使用 Collections.sort(List, Comparator>); 对其进行排序。

It's a little bit odd that SortedSet as a part of the standard API breaks the contract defined in the Set interface and uses the Comparator to define equality instead of the equals method, but that's how it is.

If your actual problem is to sort a collection of collections according to the containted collections' size, you are better of with a List, which you can sort using Collections.sort(List, Comparator>);

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文