我想要一个按集合大小排序的集合的 SortedSet(在这种情况下是集合本身,但不一定是一般情况)。这似乎违反了让 Comparator 与 equals() 一致的禁令——即,两个集合可能不相等(通过具有不同的元素),但比较相同的值(因为它们具有相同数量的元素)。
从概念上讲,我也可以使用比较器方法对大小相等的集合进行排序,但是使用排序不会利用这一点,并且没有真正有用+直观的方法来比较大小相等的集合(至少,在我的特殊情况下),所以这似乎是一种浪费。
这种不一致的情况似乎是个问题吗?
我想要一个按集合大小排序的集合的 SortedSet(在这种情况下是集合本身,但不一定是一般情况)。这似乎违反了让 Comparator 与 equals() 一致的禁令——即,两个集合可能不相等(通过具有不同的元素),但比较相同的值(因为它们具有相同数量的元素)。
从概念上讲,我也可以使用比较器方法对大小相等的集合进行排序,但是使用排序不会利用这一点,并且没有真正有用+直观的方法来比较大小相等的集合(至少,在我的特殊情况下),所以这似乎是一种浪费。
这种不一致的情况似乎是个问题吗?
SortedSet
接口扩展了核心Set
,因此应该符合Set
规范中概述的合同。
实现这一目标的唯一可能方法是让您的元素的equal()
方法行为与您Comparator
的一致 - 原因是核心Set
基于相等运行,而SortedSet
基于比较运行。
例如,核心 Set 接口中定义的方法指定如果已经有一个元素的方法将返回 true 并将这个新元素作为参数add()
,则您不能将元素添加到集合中。equal()
好吧,SortedSet
不使用equal()
,它使用compareTo()
。因此,如果您的compareTo()
退货,false
即使equals()
要退货,您的元素也会被添加true
,从而违反Set
合同。
然而,这些都不是一个实际问题。SortedSet
行为总是一致的,即使compare()
vsequals()
不是。
正如 ChssPly76 在评论中所写,在两个集合具有相同大小但不相等的情况下,您可以使用 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;
}
这似乎违反了让 Comparator 与 equals() 一致的禁令——即,两个集合可能不相等(通过具有不同的元素),但比较相同的值(因为它们具有相同数量的元素)。
无论是声明(在 Javadoc 中)还是暗示,都没有要求 aComparator
与对象的boolean equals(Object)
.
请注意,Comparable
和Comparator
是具有不同用途 的不同接口。Comparable
用于定义类的“自然”顺序。equals
在这种情况下,不一致和不一致将是一个坏主意compateTo
。相反,Comparator
当您想要使用与类的自然顺序不同的顺序时,使用 a。
编辑:这是 SortedSet 的 Javadoc 中的完整段落。
请注意,如果有序集合要正确实现 Set 接口,则由有序集合维护的排序(无论是否提供显式比较器)必须与 equals 一致。(请参阅 Comparable 接口或 Comparator 接口以了解与等于一致的精确定义。)这是因为 Set 接口是根据等于操作定义的,但排序集使用其 compareTo(或比较)方法执行所有元素比较,因此从排序集的角度来看,此方法认为相等的两个元素是相等的。一个有序集合的行为是明确定义的,即使它的排序与equals不一致;它只是不遵守 Set 接口的一般约定。
我已经强调了最后一句话。关键是这样的 SortedSet 将按照您的预期工作,但某些操作的行为不会完全符合Set
规范……因为规范根据方法定义了它们的行为equals
。
所以事实上,对一致性有一个明确的要求(我的错误),但是忽略它的后果并没有你想象的那么糟糕。当然,这取决于你是否应该这样做。我估计应该没问题,只要您彻底注释代码并确保 SortedSet 不会“泄漏”。
但是,我不清楚仅查看集合“大小”的集合比较器是否会起作用……从语义的角度来看。我的意思是,你真的想说所有(比如说)2 个元素的集合都是平等的吗?这意味着您的集合只能包含一个任何给定大小的集合......
没有理由 aComparator
应该返回与 相同的结果equals()
。实际上,Comparator
引入 APIequals()
只是因为还不够:如果要对集合进行排序,则必须知道两个元素是更小还是更大。
有点奇怪的是,SortedSet 作为标准 API 的一部分打破了 Set 接口中定义的约定,并使用 Comparator 来定义相等而不是 equals 方法,但事实就是如此。
如果您的实际问题是根据包含集合的大小对集合集合进行排序,则最好使用 List,您可以使用 Collections.sort(List, Comparator>); 对其进行排序;