Card 07/ 08
Practice: Which Classes Are Safe as HashMap Keys
No overrides, equals alone, a record, and a mutable field: which can you look up by value.
A safe key overrides equals and hashCode together, from fields that never change; without overrides, a key matches only the very same object. For each of the four classes below, decide whether it can be used as a HashMap key without losing entries, all four before opening the answer.
// 1
class A { final String id; }
// 2
class B { final String id;
@Override public boolean equals(Object o) { /* compares id */ }
}
// 3
record C(String id, long pence) { }
// 4
class D { String id;
@Override public boolean equals(Object o) { /* compares id */ }
@Override public int hashCode() { return Objects.hash(id); }
}Which of the four are safe, and what goes wrong in the others?
| Class | Safe? | Why |
|---|---|---|
A | Yes, but only as identity | Neither method overridden, so a key works only if you keep the very same object |
B | No | equals without hashCode: two equal keys usually land in different buckets, the slots a map sorts keys into |
C | Yes | A record supplies both from its components, and they are final |
D | No | Both overridden correctly, and id is not final: changing it strands the entry |
A is the interesting answer. It is not broken: it behaves exactly as intended, and what it means by a key is "this particular object". That is right for something like a cache keyed on an instance, and wrong for anything you will look up by value.
D is the one people miss, because it looks like the correct version. Correct methods reading a mutable field is the same bug arriving later.
← → move through sections and cards