Why interviewers love this: it looks tiny, but it tests the equals/hashCode contract that keeps hash-based collections honest.
Question: What happens if hashCode() is not overridden?
Answer: If you do not override hashCode(), Java uses the version inherited from Object. That default is based on object identity, so two separate objects that are equal by your custom equals() may still get different hash values. In hash-based collections like HashMap and HashSet, that can make lookups fail or let duplicates slip in.
Interview-Ready Answer: I would say: if hashCode() is not overridden, the class keeps Object’s identity-based hash code. That means hash-based collections place the object using its identity hash, so if I override equals() but not hashCode(), logically equal objects can land in different buckets and lookups may fail. The key rule is: equal objects must always have the same hash code.
hashCode() method from Object.HashMap or HashSet, Java uses the hash code to choose a bucket first. A bucket is a small slot inside the hash table.equals() to confirm whether the candidate really matches.equals() but produce different hash codes, they may go to different buckets, so the collection never even compares them and the lookup fails.equals() either, then equality is just reference equality, so the default hashCode() and default equals() still agree with each other; the collection works, but only the exact same object matches.Java’s rule is simple: if a.equals(b) is true, then a.hashCode() == b.hashCode() must also be true. The reverse is not required: two different objects may share a hash code. That is called a collision.
| Scenario | Equals | Hash code | Result |
|---|---|---|---|
| Neither overridden | Identity | Identity | Consistent, but only same object matches |
| Equals only | Logical | Identity | Broken lookups, duplicate keys |
| HashCode only | Identity | Custom | Still behaves like identity equality |
| Both overridden | Logical | Logical | Correct for maps and sets |
equals().null or false.Performance-wise, a good hash gives average O(1) insert and lookup. With many collisions, operations degrade toward O(n); in Java 8 and later, a very crowded bucket can become a tree bin, which improves that worst case to about O(log n) for that bucket. That treeification kicks in only when the table is large enough, typically with a bucket size around 8 and a capacity of at least 64; it can switch back down when the bin becomes small again.
equals() or hashCode() change after insertion, the object may become unreachable in the map.equals() after matching the bucket.HashMap; HashSet, LinkedHashMap, and many cache libraries rely on the same contract.Memory hook: Think of a hash code as the shelf number in a library, and equals() as the book title check. If two identical books get different shelf numbers, the librarian never finds the second copy when searching for the first.
Real-World Example: In a checkout service, a team uses a custom OrderKey as the key in a HashMap that tracks idempotency, meaning "do not process the same payment twice." They override equals() to compare merchantId and orderId, but forget hashCode().
OrderKey instance with the same field values.What the team sees: logs like cache miss for orderId=12345, rising map size, and inconsistent behavior where the same request sometimes appears to be new and sometimes not, depending on whether the same object reference is reused. The root cause is not the map itself; it is a broken equality contract on the key class.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Main {
static final class BadKey {
private final int id;
BadKey(int id) {
this.id = id;
}
// Logical equality is based on the business value.
// But because hashCode() is NOT overridden, HashMap/HashSet will use Object's identity hash.
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BadKey)) {
return false;
}
BadKey other = (BadKey) o;
return id == other.id;
}
@Override
public String toString() {
return "BadKey(" + id + ")";
}
}
public static void main(String[] args) {
BadKey a = new BadKey(42);
BadKey b = new BadKey(42);
System.out.println("a.equals(b) = " + a.equals(b));
System.out.println("a.hashCode() = " + a.hashCode());
System.out.println("b.hashCode() = " + b.hashCode());
System.out.println("Same logical key, but different object references.");
Map<BadKey, String> map = new HashMap<>();
map.put(a, "stored with a");
// Same reference works because the hash and reference both match.
System.out.println("map.get(a) = " + map.get(a));
// This is the failure path: b is logically equal to a, but its default hash code may point to a different bucket.
// That means HashMap may not even call equals() against the stored key.
System.out.println("map.get(b) = " + map.get(b));
System.out.println("map.containsKey(b) = " + map.containsKey(b));
Set<BadKey> set = new HashSet<>();
set.add(a);
set.add(b);
// If the identity hashes differ, the set can end up with two entries that are logically equal by equals().
// Even if a rare hash collision changes the exact number, the key lesson is that the contract is broken.
System.out.println("set size = " + set.size());
System.out.println("set contains new BadKey(42) = " + set.contains(new BadKey(42)));
BadKey sameRef = a;
System.out.println("map.get(sameRef) = " + map.get(sameRef));
}
}Follow-up & Tricky Questions:
equals(). If equal objects hashed differently, the collection could search the wrong bucket and miss a valid match.HashMap when collisions happen? Multiple keys land in the same bucket, and Java checks them with equals(). In Java 8+, a very crowded bucket may become a tree to keep lookups faster.equals() to tell them apart.equals() or hashCode() change after insertion, the object may be stored in the wrong bucket and become impossible to find.hashCode() correctly? Use the same fields that define equality, keep it consistent with equals(), and prefer immutable key fields. Utility helpers like Objects.hash(...) are fine for many cases, though manual hashing can be faster in hot paths.hashCode() but not equals(), is that enough? No. The collection will still use reference equality, so two logically same objects will not match unless they are the exact same instance.equals() to prove they really match.Object.hashCode() return the memory address? Not necessarily. The JVM only guarantees an identity-based value for that object during the run; the exact implementation is not part of the Java language contract.Common Mistakes:
equals() only: correction: always override hashCode() too when logical equality changes.equals() is the final check.Memory Hook: Hash code is the apartment number; equals() is the face at the door. If two equal people are assigned different apartment numbers, the visitor never reaches the right door.
Cheat Sheet:
Object.hashCode() is the default if you do nothing.equals() says two objects are equal, their hash codes must match.HashMap and HashSet lookups.O(1); bad collisions can push it toward O(n).Practice Tasks:
equals() only, then prove a HashMap lookup can fail.hashCode() and rerun the same test.HashSet, change the field, and observe the lookup bug.