繁体   English   中英

可变集合是否应该重写equals和hashCode?

[英]Should mutable collections override equals and hashCode?

我只是想知道为可变集合重写equalshashCode是否是个好主意。 这意味着如果我将这样的集合插入HashSet然后修改集合, HashSet将无法再找到集合。 这是否意味着只有不可变集合才能覆盖equalshashCode ,或者这是一个令人讨厌的Java程序员只是生活在一起?

如果您的类应该像值类型一样,则应该重写equalshashCode 这通常不是收藏品的情况。

(我的Java经验并不多。这个答案基于C#。)

深度和浅等的问题比Java大; 所有面向对象的语言都必​​须关注它。

添加到集合的对象应该重写equals和hash代码,但是集合接口的抽象实现中内置的默认行为足以满足集合本身的要求。

它与任何可变类都相同。 当您将实例插入HashSet然后调用变异方法时,您将遇到麻烦。 所以,我的答案是:是的,如果有用的话。

在将其添加到HashSet之前,您当然可以为Collection使用不可变的Wrapper。

我认为更大的问题是如果有人试图将一个FredCollection实例添加到Set两次会发生什么。

FredCollection c = ...
set.add(c);
set.add(c);

在此之后, setsize()应该是2还是1

你是否需要测试两个不同FredCollection实例的“相等性”? 我认为这个问题的答案在确定你的equals() / hashcode()行为时比其他任何事情更重要。

这不仅仅是集合的问题,而是一般的可变对象(另一个例子: Point2D )。 是的,这是Java程序员最终学会考虑的一个潜在问题。

您不应该重写equals和hashCode,以便它们反映可变成员。

更多是我个人的观点。 我认为哈希码和等号是不应该用于实现业务逻辑的技术术语。 想象一下:你有两个对象(不仅是集合)并询问它们是否相等,然后有两种不同的方法来回答它们:

  • 技术:该是平等的,如果它们表示相同的对象,这是从不同的相同的对象(如果你认为代理,serilization,远程的东西...的)
  • bussines逻辑:如果看起来相同(相同的属性)它们是相等的 - 这里重要的是,即使在一个应用程序中,即使对于同一个类,也没有平等的圣洁定义。 (示例问题:两块石头何时相等?))

但是因为equals是由技术人员(HashMap)使用的,所以你应该以技术方式实现它,并通过别的东西构建与业务逻辑相关的东西(比如比较器接口)。 对于您的集合,它意味着:不要覆盖equals和hashCode(以打破技术合同的方式:

注意:如果将可变对象用作映射键,则必须非常小心。 如果在对象是地图中的键的情况下以影响等于比较的方式更改对象的值,则不指定映射的行为。

(java doc of Map))。

equalshashCode一个基本困难是有两种逻辑方式可以定义等价关系; 一个类的一些消费者会想要一个定义,而同一个类的其他消费者会想要另一个。

我将如下定义两个等价关系:

  • 如果使用对Y的引用覆盖X将不会改变X或Y的任何成员的当前或未来行为,则两个对象引用X和Y完全等效。

  • 如果在一个程序中没有持久保存从与身份相关的哈希函数返回的值,那么两个对象引用X和Y具有等效状态,将所有对X的引用交换为所有对Y的引用将使程序状态保持不变。

请注意,第二个定义主要与常见场景相关,其中两个东西包含对某些可变类型(例如数组)的对象的引用,但可以确定,至少在某个特定的感兴趣的时间范围内,这些对象不是会接触到任何可能会改变它们的东西。 在这种情况下,如果“持有者”对象在所有其他方面都是等同的,则它们的等同性应取决于它们所持有的对象是否满足上述等价的第二定义。

请注意,第二个定义并不涉及对象状态可能如何变化的任何细节。 进一步注意,对于等价定义,不可变对象可以将具有相同内容的不同对象报告为相等或不相等(如果X和Y不同的唯一方式是X.Equals(X)在X.Equals(Y)时报告为真。 )报告错误,这将是一个区别,但让这些对象使用引用标识作为第一个等价关系和其他方面的等同性可能是最有用的。

不幸的是,因为Java只提供了一对等价定义类,所以类设计者必须猜测等价的哪个定义与该类的消费者最相关。 虽然有一个充分的论据支持总是使用第一个,但第二个通常更实用。 第二个问题的最大问题是类无法知道使用该类的代码何时需要第一个等价关系。

equals用于添加/删除集合中的元素,如CopyOnWriteArraySet,HashSet,如果hashCode对于两个不同的对象相等,则等于需要是对称的,即如果B.equals(C)返回true,则C.equals(B)应该返回同样的结果。 否则,您在这些XXXSets上添加/删除的行为会让人感到困惑。 检查CopyOnWriteArraySet.add的覆盖等于并删除有关如何不正确覆盖equals影响对集合的添加/删除操作

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM