简体   繁体   English

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

[英]Should mutable collections override equals and hashCode?

I was just wondering if it was a good idea to override equals and hashCode for mutable collections. 我只是想知道为可变集合重写equalshashCode是否是个好主意。 This would imply that if I insert such a collection into a HashSet and then modify the collection, the HashSet would no longer be able to find the collection. 这意味着如果我将这样的集合插入HashSet然后修改集合, HashSet将无法再找到集合。 Does this imply that only immutable collections should override equals and hashCode , or is this a nuisance Java programmers simply live with? 这是否意味着只有不可变集合才能覆盖equalshashCode ,或者这是一个令人讨厌的Java程序员只是生活在一起?

You should override equals and hashCode if your class should act like it were a value type. 如果您的类应该像值类型一样,则应该重写equalshashCode This usually is not the case for collections. 这通常不是收藏品的情况。

(I don't really have much Java experience. This answer is based on C#.) (我的Java经验并不多。这个答案基于C#。)

The problem of deep and shallow equals is bigger than Java; 深度和浅等的问题比Java大; all object oriented languages have to concern themselves with it. 所有面向对象的语言都必​​须关注它。

The objects that you add to the collection should override equals and hash code, but the default behavior built into the abstract implementation of the collection interface suffices for the collection itself. 添加到集合的对象应该重写equals和hash代码,但是集合接口的抽象实现中内置的默认行为足以满足集合本身的要求。

It's the same as with any mutable class. 它与任何可变类都相同。 When you insert an instance into a HashSet and then call a mutating method, you will get into trouble. 当您将实例插入HashSet然后调用变异方法时,您将遇到麻烦。 So, my answer is: Yes, if there's a use for it. 所以,我的答案是:是的,如果有用的话。

You can of course use an immutable Wrapper for your Collection before adding it to the HashSet. 在将其添加到HashSet之前,您当然可以为Collection使用不可变的Wrapper。

I think the bigger question is what should happen if someone attempts to add an instance of your FredCollection to a Set twice. 我认为更大的问题是如果有人试图将一个FredCollection实例添加到Set两次会发生什么。

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

Should the size() of set be 2 or 1 after this? 在此之后, setsize()应该是2还是1

Will you ever have a need to test the "equality" of two different instances of FredCollection ? 你是否需要测试两个不同FredCollection实例的“相等性”? I think the answer to this question is more important at determining your equals() / hashcode() behavior than anything else. 我认为这个问题的答案在确定你的equals() / hashcode()行为时比其他任何事情更重要。

This is not just an issue for collections, but for mutable objects in general (another example: Point2D ). 这不仅仅是集合的问题,而是一般的可变对象(另一个例子: Point2D )。 And yes, it is a potential problem that Java programmers eventually learn to take into account. 是的,这是Java程序员最终学会考虑的一个潜在问题。

You should not override equals and hashCode so that they reflect the mutable member. 您不应该重写equals和hashCode,以便它们反映可变成员。

Is more my personal point of view. 更多是我个人的观点。 I think hash code and equals are technical terms that should not be used to implement business logic. 我认为哈希码和等号是不应该用于实现业务逻辑的技术术语。 Imagine: you have two Objects (not only Collections) and ask if they are equals, then there are two different ways to answer them: 想象一下:你有两个对象(不仅是集合)并询问它们是否相等,然后有两种不同的方法来回答它们:

  • technical: the are equals if they represent the same object, which is different from being the same object (if you think of proxies, serilization, remote stuff...) 技术:该是平等的,如果它们表示相同的对象,这是从不同的相同的对象(如果你认为代理,serilization,远程的东西...的)
  • bussines logic: they are equals if the look the same (same attribute) – the important thing here is, that there is not the holy one definition of equality even to the same class in even one application. bussines逻辑:如果看起来相同(相同的属性)它们是相等的 - 这里重要的是,即使在一个应用程序中,即使对于同一个类,也没有平等的圣洁定义。 (Sample question: when are two stones equals?)) (示例问题:两块石头何时相等?))

But because equals is used by technical stuff (HashMap), you should implement it in a technical way, and build the business logic related equals by something else (something like the comparator interface). 但是因为equals是由技术人员(HashMap)使用的,所以你应该以技术方式实现它,并通过别的东西构建与业务逻辑相关的东西(比如比较器接口)。 And for your collection it means: do not override equals and hashCode (in a way that breaks the technical contract: 对于您的集合,它意味着:不要覆盖equals和hashCode(以打破技术合同的方式:

Note: great care must be exercised if mutable objects are used as map keys. 注意:如果将可变对象用作映射键,则必须非常小心。 The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. 如果在对象是地图中的键的情况下以影响等于比较的方式更改对象的值,则不指定映射的行为。

(java doc of Map) ). (java doc of Map))。

A fundamental difficulty with equals and hashCode is that there are two logical ways one may define an equivalence relation; equalshashCode一个基本困难是有两种逻辑方式可以定义等价关系; some consumers of a class will want one definition, while other consumers of that same class will want another. 一个类的一些消费者会想要一个定义,而同一个类的其他消费者会想要另一个。

I would define the two equivalence relations as follows: 我将如下定义两个等价关系:

  • Two object references X and Y are fully equivalent if overwriting X with a reference to Y would not alter the present or future behavior of any members of X or Y. 如果使用对Y的引用覆盖X将不会改变X或Y的任何成员的当前或未来行为,则两个对象引用X和Y完全等效。

  • Two object references X and Y have equivalent state if, in a program which has not persisted the values returned from identity-related hash function, swapping all references to X with all references to Y would leave program state unchanged. 如果在一个程序中没有持久保存从与身份相关的哈希函数返回的值,那么两个对象引用X和Y具有等效状态,将所有对X的引用交换为所有对Y的引用将使程序状态保持不变。

Note that the second definition is primarily relevant in the common scenario where two things hold a references to objects of some mutable type (eg arrays), but can be sure that, at least within some particular time-frame of interest, those objects are not going to be exposed to anything that might mutate them. 请注意,第二个定义主要与常见场景相关,其中两个东西包含对某些可变类型(例如数组)的对象的引用,但可以确定,至少在某个特定的感兴趣的时间范围内,这些对象不是会接触到任何可能会改变它们的东西。 In such a scenario, if the "holder" objects are equivalent in all other regards, their equivalence should depend upon whether the objects they hold meet the second definition of equivalence above. 在这种情况下,如果“持有者”对象在所有其他方面都是等同的,则它们的等同性应取决于它们所持有的对象是否满足上述等价的第二定义。

Note that the second definition does not concern itself with any details of how an object's state might change. 请注意,第二个定义并不涉及对象状态可能如何变化的任何细节。 Note further that immutable objects could, for either definition of equivalence, report distinct objects with equal content as equal or unequal (if the only way in which X and Y differ is that X.Equals(X) reports true while X.Equals(Y) reports false, that would be a difference, but it would probably be most useful to have such objects use reference identity for the first equivalence relation and equivalence of other aspects for the second. 进一步注意,对于等价定义,不可变对象可以将具有相同内容的不同对象报告为相等或不相等(如果X和Y不同的唯一方式是X.Equals(X)在X.Equals(Y)时报告为真。 )报告错误,这将是一个区别,但让这些对象使用引用标识作为第一个等价关系和其他方面的等同性可能是最有用的。

Unfortunately, because Java only provides one pair of equivalence-defining classes, a class designer must guess which definition of equivalence will be most relevant to consumers of the class. 不幸的是,因为Java只提供了一对等价定义类,所以类设计者必须猜测等价的哪个定义与该类的消费者最相关。 While there's a substantial argument to be made in favor of using the first always, the second is often more practically useful. 虽然有一个充分的论据支持总是使用第一个,但第二个通常更实用。 The biggest problem with the second is that there's no way a class can know when code using the class will want the first equivalence relation. 第二个问题的最大问题是类无法知道使用该类的代码何时需要第一个等价关系。

equals is used to add/remove elements from collections like CopyOnWriteArraySet, HashSet if hashCode is equal for two different objects, etc. equals need to be symmetric ie if B.equals(C) returns true then C.equals(B) should return the same result. equals用于添加/删除集合中的元素,如CopyOnWriteArraySet,HashSet,如果hashCode对于两个不同的对象相等,则等于需要是对称的,即如果B.equals(C)返回true,则C.equals(B)应该返回同样的结果。 Otherwise your add/remove on those XXXSets behave in a confusing manner. 否则,您在这些XXXSets上添加/删除的行为会让人感到困惑。 Check Overriding equals for CopyOnWriteArraySet.add and remove for how improper overriding of equals affected add/remove operations on collections 检查CopyOnWriteArraySet.add的覆盖等于并删除有关如何不正确覆盖equals影响对集合的添加/删除操作

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

相关问题 我应该覆盖集合的hashCode()吗? - Should I override hashCode() of Collections? 当我覆盖equals()方法时,为什么要覆盖hashCode()? - Why should I override hashCode() when I override equals() method? 我应该在 HashMap,Java 中为 String 键覆盖 equals() 和 hashCode() 吗? - Should I override equals() and hashCode() for String key in HashMap, Java? 我应该总是覆盖 equals、hashcode 和 toString 方法吗? - Should I always override equals, hashcode and toString methods? 为什么我应该重写equals和hashcode方法来跟随scnerio - why should I override equals and hashcode method for following scnerio hashCode和equals for Collections.unmodifiableCollection() - hashCode and equals for Collections.unmodifiableCollection() 超类中的2个可变变量1子类上的唯一ID。 如何最好地覆盖equals和hashcode方法 - 2 Mutable variables in Superclass 1 Unique id on Subclass. How best to override the equals and hashcode methods Java collections - 覆盖equals和hashCode - Java collections - overriding equals and hashCode HashMap使用override equals和hashCode不起作用 - HashMap with override equals and hashCode not working Java集合:如果Hashmap的键是Immutable Object,那么我们是否需要重写哈希码和等于? - Java Collections: IF the key of Hashmap is Immutable Object then do we need to override hashcode and equals?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM