Am I correct in assuming that if you have an object that is contained inside a Java Set<> (or as a key in a Map<> for that matter), any fields that are used to determine identity or relation (via hashCode()
, equals()
, compareTo()
etc.) cannot be changed without causing unspecified behavior for operations on the collection? (edit: as alluded to in this other question)
(In other words, these fields should either be immutable, or you should require the object to be removed from the collection, then changed, then reinserted.)
The reason I ask is that I was reading the Hibernate Annotations reference guide and it has an example where there is a HashSet<Toy>
but the Toy
class has fields name
and serial
that are mutable and are also used in the hashCode()
calculation... a red flag went off in my head and I just wanted to make sure I understood the implications of it.
In a HashSet/HashMap, you could mutate a contained object to change the results of
compareTo()
operation -- relative comparison isn't used to locate objects. But it'd be fatal inside a TreeSet/TreeMap.You can also mutate objects that are inside an IdentityHashMap -- nothing other than object identity is used to locate contents.
Even though you can do these things with these qualifications, they make your code more fragile. What if someone wants to change to a TreeSet later, or add that mutable field to the hashCode/equality test?
That is correct, it can cause some problems locating the map entry. Officially the behavior is undefined, so if you add it to a hashset or as a key in a hashmap, you should not be changing it.
Yes, that will cause bad things to happen.
The javadoc for
Set
saysThis simply means you can use mutable objects in a set, and even change them. You just should make sure the change doesn't impact the way the
Set
finds the items. ForHashSet
, that would require not changing the fields used for calculatinghashCode()
.