为什么AbstractCollection没有实现equals()方法?

7

你知道吗:

Map<Object,Object> m1 = new HashMap<Object, Object>();
Map<Object,Object> m2 = new HashMap<Object, Object>();
System.out.println("m1.equals(m2) = "+m1.equals(m2));
System.out.println("m1.keySet().equals(m2.keySet()) = "
            +m1.keySet().equals(m2.keySet()));
System.out.println("m1.entrySet().equals(m2.entrySet()) = "
            +m1.entrySet().equals(m2.entrySet()));
System.out.println("m1.values().equals(m2.values()) = "
            +m1.values().equals(m2.values()));

将会输出:

m1.equals(m2) = true
m1.keySet().equals(m2.keySet()) = true
m1.entrySet().equals(m2.entrySet()) = true
m1.values().equals(m2.values()) = false

这是因为AbstractCollectionHashMap$Values继承自它)没有覆盖#equals()方法导致的。
你有想法为什么会这样吗?
3个回答

6
根据Collection#equals()的合同,Collection没有通用的equals()方法,因此AbstractCollection无法提供该方法。
请注意,HashMap$Values既不是Set也不是List,这就是它不支持equals()的困境所在。

我喜欢你的回答,你用更好的方式说了与我类似的内容。Values不是Set或List,但可以将其视为Bag。为什么不使用bag语义来实现equals()呢? - Craig P. Motlin
@Motlin:我同意我们的答案很相似,不幸的是我们几乎同时回答了。就包语义而言,一个可交换的containsAll()检查会破坏List.equals()和Set.equals()的契约(因为它们本身不再是可交换的)。 - Greg Case

4
AbstractList和AbstractSet都扩展了AbstractCollection,并且它们的equals()方法具有不同的行为,由ListSet接口指定。Collection的接口说:
尽管Collection接口对Object.equals的一般约定没有添加规定,但是直接实现Collection接口(换句话说,创建一个既不是Set也不是List但是是一个Collection的类)的程序员如果选择重写Object.equals,则必须小心谨慎。
因此,AbstractCollection绝不能覆盖equals()。 话虽如此,我不太清楚HashMap$Values为什么不自己实现equals()。

很好的回答。也许我会问一个更具体的问题,关于为什么HashMap$Values没有实现equals()方法(可能是因为没有人需要它)。我认为我会验证Greg Case的答案,因为你似乎更喜欢它的措辞。 - Michel

0

我不确定这是否是官方原因,但AbstractCollection避免对潜在子类添加语义限制。相等性的语义由具体继承数据结构的性质决定,特别是基于您的结构是否有序以及它是否允许重复。

例如,考虑TreeSet、LinkedList、Bag等。

顺便问一下,在您发布的代码中,values返回的实际类型是什么?那应该是一个具有具体实现的子类。如果您运行此代码时映射为空,则可能会得到一些不将两个空集视为相等的内容。


问题提到values()返回的类型是HashMap$Values。 - Craig P. Motlin
没错,但它是什么?它是一个自定义数据结构还是现有的数据结构?我猜它是某种迭代器? - Uri

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