Card 06/ 08

GotchaDifficulty: Advanced2 min

Mutable HashMap Keys: Changing a Key Loses the Entry

Both methods are correct, one field changes after the put, and the entry is gone.

A HashMap files an entry in a bucket, one of its internal slots, chosen by the key's hash code, the number its hashCode() returns, at the moment of put. If a field that hashCode reads changes afterwards, the key hashes to a different bucket and the entry cannot be found. Keys should be immutable, unable to change after they are created.

Both methods are overridden correctly, from the same field. The entry goes in, one field on the key object is updated, and the entry is gone:

java
Order key = new Order("AB-1");
Map<Order, String> map = new HashMap<>();
map.put(key, "paid");

key.reference = "AB-2";

System.out.println(map.get(key));
System.out.println(map.get(new Order("AB-1")));
System.out.println(map.size());
text
null
null
1

The bucket was chosen when put ran, from the hash of "AB-1". Changing the field changed the hash and did not move the entry, so the key now hashes to a bucket it is not in, and a fresh "AB-1" key finds the right bucket but no longer matches with equals.

Put this beside a key class that overrides equals but not hashCode, where an equal key also finds nothing, and the shared fact comes out: the map looked in a bucket the entry is not in, and reported the key as absent rather than failing. One got there by hashing differently from the start; this one got there by hashing differently later. The entry is unreachable either way, and size() still counts it.

Use immutable keys, or keys whose equals and hashCode read only fields that never change. A record with final components is the shape that makes this impossible rather than merely unlikely.