Objects that serve as keys in ordered sets and maps should be immutable. When some fields must be mutable, the
compareTo() methods must consider only immutable state when comparing objects. Violations of this rule can produce inconsistent orderings in collections. The documentation of
java.util.Interface Set<E> and
java.util.Interface Map<K,V> warns against this. For example, the documentation for the Interface
Map states [API 2014]:
Note: great care must be exercised [when] 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
equalscomparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the
hashCodemethods are no longer well defined on such a map.
Noncompliant Code Example
This noncompliant code example defines a mutable class
Employee that consists of the fields
salary, whose values can be changed using the
setSalary() method. The
equals() method is overridden to provide a comparison facility by employee name.
Use of the
Employee object as a key to the map is insecure because the properties of the object could change after an ordering has been established. For example, a client could modify the
name field when the last name of an employee changes. As a result, clients would observe nondeterministic behavior.
This compliant solution adds a final field
employeeID that is immutable after initialization. The
equals() method compares
Employee objects on the basis of this field.
Employee class can now be safely used as a key for the map in the client code.
Noncompliant Code Example
Many programmers are surprised by an instance of hash code mutability that arises because of serialization. The contract for the
hashCode() method lacks any requirement that hash codes remain consistent across different executions of an application. Similarly, when an object is serialized and subsequently deserialized, its hash code after deserialization may be inconsistent with its original hash code.
This noncompliant code example uses the
MyKey class as the key index for the
MyKey class overrides
Object.equals() but uses the default
Object.hashCode(). According to the Java API [API 2014] class
To successfully store and retrieve objects from a hash table, the objects used as keys must implement the
hashCodemethod and the
This noncompliant code example follows that advice but nevertheless can fail after serialization and deserialization. Consequently, it may be impossible to retrieve the value of the object after deserialization by using the original key.
This compliant solution changes the type of the key value to be an
Integer object. Consequently, key values remain consistent across multiple runs of the program, across serialization and deserialization, and also across multiple Java Virtual Machines.
This problem could also have been avoided by overriding the
hashcode() method in the
MyKey class, though it is best to avoid serializing hash tables that are known to use implementation-defined parameters.
Failure to ensure that the keys used in a comparison operation are immutable can lead to nondeterministic behavior.
Some available static analysis tools can detect instances where the
compareTo() method is reading from a nonconstant field.