是否有可能TreeSet等于HashSet但不是HashSet等于TreeSet

时间:2020-06-19 19:08:48

标签: java collections hashset treeset

我今天进行了一次采访,接受采访的人对他的陈述感到困惑,他询问TreeSet是否等于HashSet但不等于HashSet等于TreeSet。我说“不”,但根据他的回答是“是”。

怎么可能?

5 个答案:

答案 0 :(得分:68)

您的面试官是正确的,他们在某些特定情况下不具有对等关系。 TreeSet可能等于HashSet,而反之则不然。这是一个示例:

TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
HashSet<String> hashSet = new HashSet<>();
treeSet.addAll(List.of("A", "b"));
hashSet.addAll(List.of("A", "B"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true

其原因是TreeSet使用比较器来确定元素是否重复,而HashSet使用equals

引用TreeSet

请注意,如果要正确实现Set接口,则集合(无论是否提供了显式比较器)保持的顺序 必须与equals 保持一致。

答案 1 :(得分:19)

在不违反等值合约或Set合约的情况下是不可能的。 Java中的equals定义要求对称,即a.equals(b)必须与b.equals(a)相同。

实际上,very documentation of Set

如果指定对象也是一个集合,并且两个集合具有相同的大小,并且指定集合的​​每个成员都包含在此集合中(或者等效地,这个集合的每个成员都包含在指定集合中),则返回true 。此定义可确保equals方法可在set接口的不同实现中正常工作。

答案 2 :(得分:8)

,在不违反Object类的equals方法的一般协定的情况下是不可能的,这需要对称,即。 e。 x.equals(y)当且仅当y.equals(x)

BUT ,类TreeSetHashSet分别实现Set接口的equals协定。除其他事项外,此合同要求指定集合中的每个成员都包含在该集合中。要确定元素是否在集合中,将调用contains方法,对于TreeSet使用Comparator,对于HashSet使用hashCode

最后:

,在某些情况下是可能的。

答案 3 :(得分:2)

这是《 Java Generics and Collections》一书的引文:

原则上,客户应了解的只是如何保持 合同一方;如果无法做到这一点,所有的赌注都关闭, 无需确切说明供​​应商将做什么。

答案是:是的,只有当您不遵守Java合同时,它才会发生。在这里,您可以说Java违反了平等的对称属性,但是如果发生这种情况时,请确保您是最先破坏了某些其他接口契约的人。 Java已经记录了这种行为。

通常,您应该阅读ComparatorComparable接口的文档,以便在排序的集合中正确使用它们。

有效的Java第三版第14页第66-68页以某种方式回答了这个问题。

这是本书中为实现Comparable接口定义合同时的引文(请注意,这只是整个合同的一部分):

•强烈建议(但不是必需)(x.compareTo(y) == 0) ==(x.equals(y))。一般而言,任何实现Comparable接口且违反此条件的类都应明确 表明这一事实。推荐的语言是“注意:该课程有 与等式不一致的自然顺序。”

上面写着强烈建议但不是必需,这意味着您可以参加 x.compareTo(y)==0并不意味着x.equal(y)==true。(但是,如果以这种方式实现,则不能将它们用作排序集合中的元素,BigDecimal就是这种情况)

书中描述Comparable接口合同这一部分的段落值得一提:

这只是一个强烈的建议,而不是一个真正的要求 声明compareTo方法强加的相等性测试 通常返回与equals方法相同的结果。如果这 遵守规定,由compareTo方法施加的顺序为 说等于平等。如果违反,则排序为 说与平等不一致。一个类的compareTo方法 施加与等式不一致的命令仍然可以使用,但是 包含类元素的已排序集合可能不服从 适当的收集接口的一般合同 (集合,集合或地图)。这是因为 这些接口是根据equals方法定义的,但已排序 合集使用由compareTo施加的相等性检验代替 等于。如果发生这种情况,这不是灾难,但是 注意。

实际上,我们在Java本身中有一些不遵循此建议的类。 BigDecimal是其中之一,本书中对此进行了提及。

例如,考虑BigDecimal类,其compareTo方法为 与平等不一致。如果您创建一个空的HashSet实例,并且 然后添加新的BigDecimal(“ 1.0”)和new BigDecimal(“ 1.00”), 将包含两个元素,因为添加了两个BigDecimal实例 使用equals方法进行比较时,该集合的不等式。如果, 但是,您可以使用TreeSet而不是 HashSet,该集合将仅包含一个元素,因为这两个元素 使用compareTo比较时,BigDecimal实例相等 方法。 (有关详细信息,请参见BigDecimal文档。)

但是,此行为记录在BigDecimal文档中。让我们看一下文档的那一部分:

注意:如果将BigDecimal对象用作键,则应格外小心 自BigDecimal的自然状态以来,在SortedMap或SortedSet中的元素中 顺序与等号不一致。参见Comparable,SortedMap或 SortedSet了解更多信息。

因此,尽管您可以像下面那样编写代码,但是您不应该这样做,因为BigDecimal类禁止了这种用法:

Set<BigDecimal> treeSet = new TreeSet<>();
Set<BigDecimal> hashSet = new HashSet<>();
treeSet.add(new BigDecimal("1.00"));
treeSet.add(new BigDecimal("2.0"));
hashSet.add(new BigDecimal("1.00"));
hashSet.add(new BigDecimal("2.00"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true

请注意,当您未将任何比较器传递给ComparableTreeSet时,TreeMap将被用作元素的自然排序,而当您传递{{ 1}}到那些类的构造函数。 Comparator文档中对此进行了提及:

表示比较器c对一组元素S施加的排序 当且仅当c.compare(e1,e2)== 0具有 S中每个e1和e2的布尔值都与e1.equals(e2)相同。

使用具有以下功能的比较器时应格外小心: 施加与等于不一致的排序来排序已排序的集合 (或排序的地图)。假设有一个显式的排序集(或排序图) 比较器c与从集合S提取的元素(或键)一起使用。 c对S施加的排序与等于不一致,排序 集合(或排序的地图)的行为“奇怪”。特别是排序 集(或排序后的地图)将违反集(或 地图),以等式定义。

因此,考虑到Comparator的这种记录,@ Aniket Sahrawat给出的以下示例不起作用:

Comparator

简而言之,答案是:是的,它会发生,但仅当您破坏上述接口之一(TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); HashSet<String> hashSet = new HashSet<>(); treeSet.addAll(List.of("A", "b")); hashSet.addAll(List.of("A", "B")); System.out.println(hashSet.equals(treeSet)); // false System.out.println(treeSet.equals(hashSet)); // true SortedSetComparable)的书面合同时才会发生

答案 4 :(得分:1)

已经有了很好的答案,但是我想从更一般的角度来解决这个问题。

在数学,逻辑以及相应的计算机科学中,“等于” Symmetric Binary Relation,这意味着如果A is equal to B则{{ 1}}。

因此,如果B is equal to A 等于 TreeSet X,则HashSet Y 必须等于 HashSet Y,并且必须始终为真。

但是,如果违反了平等的对称属性(即平等的实现不正确),那么TreeSet X可能并不意味着{{1} }。


Java Object#equals方法的文档明确指出:

equals方法对非null对象引用实现等效关系。

因此,它implements对称属性,如果不是,那么它violates通常等于[Equal],并且violates是对象# equals方法,特别是在Java中。