hashCode() contract in Java
Equal objects must have equal hash codes; override both together.
HashSet, then ask for an equal Point. The set says "never heard of it". Your equals is perfect. So what's missing?Buckets and coat hooks
Hash collections work like a coat check. **hashCode() picks the hook (bucket); only then does equals compare the items hanging there. If two equal objects get different hash codes, the set looks on the wrong hook** and equals is never even called.
The golden rule
**If a.equals(b), then a.hashCode() == b.hashCode().** So whenever you override equals, override hashCode too, using the same fields. Objects.hash(...) makes that easy.
@Override
public int hashCode() {
return Objects.hash(name, age);
}equals without hashCode
Override only equals and you inherit Object's identity-based hashCode. Two equal Pts almost always get different hash codes, so set.contains(new Pt(1)) returns false — even right after set.add(new Pt(1)).
A famous collision
What does this print?
int h1 = "Aa".hashCode(), h2 = "BB".hashCode();
System.out.println(h1 == h2);
System.out.println("Aa".equals("BB"));true falsefalse falsetrue true
Show the answer
true, then false. Same hash code does not mean equal — different objects can collide on the same int. That's fine: equals settles the question.
Both overridden
Pt overrides both equals and hashCode. What prints?
// Pt overrides equals AND hashCode,
// both based on its x field
Set<Pt> s = new HashSet<>();
s.add(new Pt(1));
s.add(new Pt(1));
System.out.println(s.size());
System.out.println(s.contains(new Pt(1)));2 false1 true2 true
Show the answer
1, then true. The second add finds an equal element on the same hook and is ignored, and a fresh Pt(1) is found.
Choosing a hashCode
public int hashCode() {
return 42;
}Correct but every object lands in one bucket: lookups crawl. super.hashCode() (identity) or random values break the contract outright.
public int hashCode() {
return Objects.hash(name, age);
}Same fields as equals: equal objects match, others spread out.
In real projects
A sneaky production bug: using an object as a HashMap key, then mutating a field that feeds hashCode. The entry is now on the wrong hook — get returns null and the entry is effectively lost. Keep keys immutable.
Key takeaways
- a.equals(b) means a.hashCode() == b.hashCode()
- Same hash code does NOT imply equal
- Override equals and hashCode together
- Objects.hash(f1, f2) is an easy correct implementation
String's hash formula is part of its Javadoc: s[0]*31^(n-1) + ... + s[n-1]. That's why "Aa" and "BB" (both 2112) collide on every JVM in the world.
Practice questions
Pt overrides equals (comparing x) but not hashCode. After set.add(new Pt(1)), set.contains(new Pt(1)) returns false. Why?
- The two equal objects have different identity-based hash codes, so the set searches the wrong bucket
- HashSet only uses ==, never equals
- contains() requires Pt to implement Comparable
- The first Pt was garbage-collected
Check your answer
The two equal objects have different identity-based hash codes, so the set searches the wrong bucket. Without hashCode, Object's identity-based version is used, so equal Pts almost always get different hashes and equals is never even called.
Pt overrides both equals and hashCode using its x field. What does this print?
var a = new Pt(1);
var b = new Pt(1);
Set<Pt> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println(set.size());
System.out.println(set.contains(new Pt(1)));- 2 false
- 1 true
- 2 true
- 1 false
Check your answer
1 true. With matching equals and hashCode, the second add finds an equal element in the same bucket and is ignored, and a fresh Pt(1) is found.