Careeroza
Broken hashCode in HashMap
How HashMap Uses Them
map.get(key)
│ 1. key.hashCode() → choose bucket
▼
bucket 0 │ bucket 1 │ bucket 2 ─► [k1=v1] ─► [k2=v2] (linked list / tree)
│ 2. key.equals(k?) → find the entryIf you override equals but not hashCode, two equal keys usually land in different buckets, so the map never finds the second one.
The Bug
Example
BadSku overrides only equals. The set keeps "duplicates" and lookups fail.
import java.util.*;
public class Main {
public static void main(String[] args) {
Set<BadSku> bad = new HashSet<>();
bad.add(new BadSku("PHN-01"));
bad.add(new BadSku("PHN-01"));
System.out.println("BadSku set size: " + bad.size() + " (should be 1)");
System.out.println("contains? " + bad.contains(new BadSku("PHN-01")));
Set<GoodSku> good = new HashSet<>();
good.add(new GoodSku("PHN-01"));
good.add(new GoodSku("PHN-01"));
System.out.println("GoodSku set size: " + good.size());
System.out.println("contains? " + good.contains(new GoodSku("PHN-01")));
}
}
class BadSku {
final String code;
BadSku(String code) { this.code = code; }
@Override public boolean equals(Object o) { return o instanceof BadSku b && b.code.equals(code); }
// hashCode() NOT overridden: identity hash, different for each object
}
record GoodSku(String code) {}Output
BadSku set size: 2 (should be 1) contains? false GoodSku set size: 1 contains? true
Mutable Keys
Example
Changing a field used in hashCode after inserting the key "loses" the entry. Use immutable keys.
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<MutableKey, String> map = new HashMap<>();
MutableKey key = new MutableKey("A");
map.put(key, "order data");
key.id = "B"; // hash changes
System.out.println("get(key): " + map.get(key));
System.out.println("size: " + map.size());
}
}
class MutableKey {
String id;
MutableKey(String id) { this.id = id; }
@Override public boolean equals(Object o) { return o instanceof MutableKey k && k.id.equals(id); }
@Override public int hashCode() { return id.hashCode(); }
}Output
get(key): null size: 1