Situation
I needed to overwrite equals()
and as it is recommended I also overwrote the hashCode()
method using the same fields. Then, when I was looking at a set, that contained only the one object I got the frustrating result of
set.contains(object)
=> false
while
set.stream().findFirst().get().equals(object)
=> true
I understand now, that this is due to changes that were made to object
after it was added to set
which again changed its hashCode. contains
then looks at the wrong key and can't find the object
.
My requirements for the implementation are
- mutable fields are needed to correctly implement
equals()
- use these objects safely in hash-based
Collections
orMaps
such ashHashSet
even if they are prone to changes.
which conflicts with the convention that
equals()
andhashCode()
should use the same fields in order to avoid surprises (as argued here: https://stackoverflow.com/a/22827702).
Question
Are there any dangers to using only a subset of fields which are used in equals()
to calculate hashCode()
instead of using all?
More specifically this would mean: equals()
uses a number of fields of the object whereas hashCode()
only uses those fields that are used in equals()
and that are immutable.
I think this should be okay, because
- the contract is fullfilled: equal objects will produce the same hashCode, while the same hashCode does not necesairly mean that the objects are the same.
- The hashCode of an object stays the same, even if an object is exposed to changes and therefore will be found in a
HashSet
before and after those changes.
Related posts that helped me understand my problem but not how to solve it: What issues should be considered when overriding equals and hashCode in Java? and Different fields for equals and hashcode
That's perfectly valid to me. Suppose you have a
Person
:name
decides where the entry will go (thinkHashMap
) or which bucket will be chosen.You put a
Person
as aKey
insideHashMap
: according tohashcode
it goes to some bucket, second for example. You upgrade theincome
and search for thatPerson
in the map. According tohashcode
it must be in the second bucket, but according toequals
it's not there:And a test:
What you are missing I think is the fact that
hashcode
decides a bucket where an entry goes (there could be many entries in that bucket) and that's the first step, butequals
decides if that is well, an equal entry.The example that you provide is perfectly legal; but the one you linked is the other way around - it uses more fields in
hashcode
making it thus incorrect.If you understand these details that first hashcode is used to understand where and Entry might reside and only later all of them (from the subset or bucket) are tried to be found via
equal
- your example would make sense.