Java SortedSet +比较器,与 equals() 的一致性问题
我想要一个集合的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
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 coreSet
and thus should conform to the contract outlined inSet
specification.The only possible way to achieve that is to have your element's
equal()
method behavior be consistent with yourComparator
- the reason for that is that coreSet
operates based on equality whereasSortedSet
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 whoseequal()
method would return true with this new element as argument. Well,SortedSet
doesn't useequal()
, it usescompareTo()
. So if yourcompareTo()
returnsfalse
your element WILL be added even ifequals()
were to returntrue
, thus breaking theSet
contract.None of this is a practical problem per se, however.
SortedSet
behavior is always consistent, even ifcompare()
vsequals()
are not.正如 ChssPly76 在评论中所写,在两个 Collections 大小相同但不相等的情况下,您可以使用 hashCode 来决定compareTo 调用。这工作得很好,除非在极少数情况下,您有两个大小相同、不相等但具有相同 hashCode 的集合。诚然,这种情况发生的可能性很小,但也是可以想象的。如果您想非常小心,请使用 System.identityHashCode 而不是 hashCode。这应该为每个集合提供一个唯一的编号,并且您不应该发生冲突。
最终,这为您提供了按大小对集合中的集合进行排序的功能,并且在两个具有匹配大小的集合的情况下可以任意排序。如果这就是您所需要的,那么它并不比通常的比较慢多少。如果您需要不同 JVM 实例之间的顺序保持一致,那么这将不起作用,您必须采用其他方式。
伪代码:
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:
没有要求(在 Javadoc 中)明示或暗示,
Comparator
必须与对象的boolean equals(Object)
实现一致。请注意,
Comparable
和Comparator
是具有不同用途的不同接口。Comparable
用于定义类的“自然”顺序。在这种情况下,equals
和compateTo
不一致是一个坏主意。相比之下,当您想要使用与类的自然顺序不同的顺序时,可以使用Comparator
。编辑:这是来自 SortedSet 的 Javadoc 的完整段落。
我已经突出显示了最后一句话。关键是这样的 SortedSet 将按照您最有可能期望的方式工作,但是某些操作的行为不会完全符合
Set
规范...因为该规范根据equals
方法。所以事实上,有一个明确的一致性要求(我的错误),但忽略它的后果并不像你想象的那么糟糕。当然,是否应该这样做取决于您自己决定。在我看来,应该没问题,只要您彻底注释代码并确保 SortedSet 不会“泄漏”。
然而,我不清楚仅查看集合“大小”的集合比较器是否会起作用......从语义角度来看。我的意思是,您真的想说所有具有(例如)2 个元素的集合都是相等的吗?这意味着您的集合只能包含任何给定大小的一个集合......
There is no requirement, either stated (in the Javadoc) or implied, that a
Comparator
be consistent with an object's implementation ofboolean equals(Object)
.Note that
Comparable
andComparator
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 forequals
andcompateTo
to be inconsistent. By contrast, aComparator
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.
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 theequals
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 ...
Comparator
没有理由返回与equals()
相同的结果。事实上,引入Comparator
API 是因为equals()
还不够:如果要对集合进行排序,则必须知道两个元素是小于还是大于。There is no reason why a
Comparator
should return the same results asequals()
. In fact, theComparator
API was introduced becauseequals()
just isn't enough: If you want to sort a collection, you must know whether two elements are lesser or greater.有点奇怪的是,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>);