简体   繁体   中英

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. 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. Does this imply that only immutable collections should override equals and hashCode , or is this a nuisance Java programmers simply live with?

You should override equals and hashCode if your class should act like it were a value type. This usually is not the case for collections.

(I don't really have much Java experience. This answer is based on C#.)

The problem of deep and shallow equals is bigger than 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.

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. 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.

I think the bigger question is what should happen if someone attempts to add an instance of your FredCollection to a Set twice.

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

Should the size() of set be 2 or 1 after this?

Will you ever have a need to test the "equality" of two different instances of FredCollection ? I think the answer to this question is more important at determining your equals() / hashcode() behavior than anything else.

This is not just an issue for collections, but for mutable objects in general (another example: Point2D ). And yes, it is a potential problem that Java programmers eventually learn to take into account.

You should not override equals and hashCode so that they reflect the mutable member.

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...)
  • 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. (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). And for your collection it means: do not override equals and hashCode (in a way that breaks the technical contract:

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) ).

A fundamental difficulty with equals and hashCode is that there are two logical ways one may define an equivalence relation; 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.

  • 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.

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.

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. 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. Otherwise your add/remove on those XXXSets behave in a confusing manner. Check Overriding equals for CopyOnWriteArraySet.add and remove for how improper overriding of equals affected add/remove operations on collections

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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