Can you use immutable objects as keys in HashMap?
String computes hashCode once and caches it:
Junior Level
Not just possible, but highly recommended! Immutable objects are ideal keys for HashMap.
Map<String, Integer> ages = new HashMap<>();
ages.put("Ivan", 25); // String — immutable
ages.put("Ivan", 30); // Update by the same key
System.out.println(ages.get("Ivan")); // 30
Why this is good
hashCode()doesn’t change — object is always in the correct bucketequals()works stably- No need to worry about the key getting “lost”
Best keys
String— most popularInteger,Long— primitive wrappersLocalDate,UUID— stable identifiersRecord— composite keys from multiple fields
Middle Level
How HashMap works with keys
put(key, value)—key.hashCode()is computed → bucket is determinedget(key)—key.hashCode()is computed again → searched in the same bucket- If
hashCode()changed — the key is “lost” in a different bucket
Immutable objects cache hashCode
String computes hashCode once and caches it:
// Inside String
private int hash; // 0 by default
public int hashCode() {
if (hash == 0 && value.length > 0) {
hash = ...; // computed once
}
return hash;
}
Problem with mutable keys
List<String> key = new ArrayList<>(List.of("A"));
map.put(key, 42);
key.add("B"); // changed the key!
map.get(key); // null — hashCode changed
The object is physically in the bucket, but get searches elsewhere.
Important: equals and hashCode must use the same set of fields.
If hashCode uses id but equals uses id + name — two objects with the same id
but different names end up in the same bucket but won’t be equal.
Senior Level
Ideal key for HashMap
public record CompositeKey(String type, Long id) {} // automatically final, equals, hashCode
Map<CompositeKey, Entity> map = new HashMap<>();
map.put(new CompositeKey("user", 1L), user);
Strategy if the key must be mutable
- Use an identifier:
Long idinstead of the object itself - Remove → modify → re-add:
map.remove(key); key.changeSomething(); map.put(key, value); - IdentityHashMap — compares by
==, not byequals()/hashCode()
hashCode for composite keys
Record generates hashCode via Objects.hashCode for each field.
On collisions, HashMap builds a tree (O(log n) instead of O(n)).
For composite keys, combine hashCode of all significant fields.
Summary for Senior
- Immutable keys guarantee determinism of hash collections
- Prevent hard-to-find bugs and memory leaks
Record— ideal choice for composite keys- Design collection keys as immutable entities
- Cached
hashCode— additional optimization
Interview Cheat Sheet
Must know:
- Immutable objects are IDEAL keys: stable hashCode, no risk of loss in bucket
- String, Integer, LocalDate, Record — best keys
- String caches hashCode — computed once, speeds up repeated lookups
- Record generates equals/hashCode via
Objects.hashCodefor each field - Mutable key = lost object: hashCode changed, get searches in a different bucket
- On collisions, HashMap builds a tree — O(log n) instead of O(n)
Frequent follow-up questions:
- Why is a mutable key dangerous? — hashCode changes → object gets “lost” → logical leak
- Record as a key? — Ideal: automatically final, equals, hashCode across all fields
- If the key must be mutable? — IdentityHashMap (comparison by ==) or remove → mutate → put
- Should equals and hashCode match? — Yes, use the same set of fields
Red flags (do NOT say):
- “Mutable key in HashMap is fine” — object becomes unreachable
- “hashCode doesn’t need caching” — for immutable objects it’s an optimization
- “ArrayList as a key is a good idea” — it’s mutable, hashCode will change
- “IdentityHashMap solves all problems” — compares by ==, not equals — a specific use case
Related topics:
- [[22. What are the advantages of immutable objects for caching]]
- [[28. What happens if you modify a mutable key in HashMap]]
- [[20. What is Record and how does it help create immutable classes]]
- [[4. Why is String class immutable]]