The contract between equals and hashCode — the most commonly broken Java rule.
Published January 20, 2025
This is one of the most commonly broken rules in Java, and one of the most commonly asked interview questions.
Java's specification requires:
a.equals(b) is true, then a.hashCode() == b.hashCode() must be trueHashMap and HashSet rely on this contract:
hashCode() — O(1) bucket lookupequals() for actual equalityIf you override equals() but not hashCode(), your objects break in HashMap:
public class User {
String email;
@Override
public boolean equals(Object o) {
if (!(o instanceof User u)) return false;
return Objects.equals(email, u.email);
}
// ❌ No hashCode override!
}
Set<User> users = new HashSet<>();
users.add(new User("alice@example.com"));
users.contains(new User("alice@example.com")); // ❌ returns false!
public class User {
String email;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User u)) return false;
return Objects.equals(email, u.email);
}
@Override
public int hashCode() {
return Objects.hash(email); // use the same fields as equals
}
}
Java 16+ Records generate correct equals() and hashCode() based on all record components:
record User(String email, String name) {} // equals/hashCode generated correctly
"You must always override hashCode() when you override equals(). The rule is: if two objects are equal, they must produce the same hash code. Use Objects.hash() with the same fields you use in equals()."