为什么ConcurrentSkipListSet.contains需要比较器而不是equals

7

我正在使用ConcurrentSkipListSet并使用contains方法。

根据JAVA文档所述,contains方法返回true,如果此集合包含指定的元素。更正式地说,当且仅当此集合包含一个元素e使得o.equals(e)时,返回true。

但是经过我的测试,似乎没有使用equals方法,而是强制使用Comparator。请帮助我理解JAVA规范和实现之间的这种不一致。

ConcurrentSkipListSet

/** * 如果使用比较器,则返回ComparableUsingComparator,否则将关键字强制转换为可比较类型,可能会导致ClassCastException, * 而这个异常会传播回调用者。 */ private Comparable comparable(Object key)

在java.util.concurrent.ConcurrentSkipListMap.comparable(ConcurrentSkipListMap.java:663) 在java.util.concurrent.ConcurrentSkipListMap.doGet(ConcurrentSkipListMap.java:821) 在java.util.concurrent.ConcurrentSkipListMap.containsKey(ConcurrentSkipListMap.java:1608)

我正在使用Oracle JDK 7


这是排序集合的完全正常行为。TreeSet 的文档可能适用:“请注意,集合维护的顺序(无论是否提供了显式比较器)必须与等于关系一致,如果要正确实现 Set 接口,则必须如此。(有关与等于关系一致的精确定义,请参见 Comparable 或 Comparator。)” - Louis Wasserman
1
CSLS的Java文档应该用粗体澄清这一点。 - Amrish Pandey
@Louis Wasserman:请注意,TreeSet.contains 的文档中也有同样具有误导性的句子,显然这是在Java 6中故意添加的。 - Holger
1个回答

4
我认为有两个问题/关注点,(1) 为什么contains需要一个Comparator或Comparable。(2) Javadoc说它将使用equals方法。
ConcurrentSkipListSet是一种可导航有序集合,因此所有元素都必须保持自然顺序,或者您必须指定比较器。
我认为Javadoc的陈述不正确,或者至少是误导性的。在底层,CSLS将委托给ConcurrentSkipListMap.containsKey,因此它现在无法控制contains实现。话虽如此,我认为可以有一个论点来澄清javadocs。
编辑: 对于这些对象不可比较的事实,还有一个throws文档
ClassCastException-如果指定的元素无法与当前在此集合中的元素进行比较

2
对于这里的Java文档感到失望。从来没有想过它会如此不真实。我们倾向于认真对待它,很少通过实现进行调试来验证所有内容。 - Amrish Pandey
@Amrish Pandey:强烈建议在决定是否使用类之前,先考虑一般情况下类的目的和限制,而不是对某个特定方法的行为感到惊讶。这个类中依赖于顺序而不是相等性的方法并不仅仅是contains - Holger
1
@holger 大多数人都是根据 Java 文档而不是查找每个 API 的源代码或通过异常的艰难方式来学习它。考虑到 Java 文档被广泛参考,它应该有更新的 contains 定义。它明确指出应该使用 equals() 方法,几乎没有人会想象其他情况。 - Amrish Pandey
@Amrish Pandey:当然,你应该遵循Java文档,即在开始使用类之前,应该研究文档。如果您理解了记录的类目的,就不会在单个特定方法的错误文档上绊倒。如果您不理解ConcurrentSkipListMap对排序的依赖关系,则不应使用它。同样适用于TreeSet,它已经有同样的文档错误十年了,显然影响很小... - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接