Skip to content
SDETRole
SDETRole

Java interview question

Explain the equals() and hashCode() contract. What breaks if you violate it?

intermediate 2–5 yrs #equals#hashcode#collections#hashmap#records

Short answer

If two objects are equal by equals(), they must return the same hashCode(); unequal objects may share a hash. Hash-based collections find the bucket by hashCode and then compare with equals, so overriding only equals makes HashSet and HashMap lookups fail. Records and Objects.hash make correct implementations easy.

Answer

The contract, defined in java.lang.Object, says: if a.equals(b) is true, then a.hashCode() == b.hashCode() must also be true. The reverse is not required: unequal objects may share a hash code (a collision), it just makes hash tables slower. Additionally, equals must be reflexive, symmetric, transitive, consistent across calls, and return false for null. If you override equals, you must override hashCode.

Why it matters: how hash collections work

HashMap, HashSet and friends look up an element in two steps:

  1. Compute hashCode() to find the bucket.
  2. Within that bucket, compare candidates with equals().

If two “equal” objects have different hash codes, step 1 sends the lookup to the wrong bucket and equals is never called.

The failure, concretely

class User {
    private final String email;
    User(String email) { this.email = email; }

    @Override
    public boolean equals(Object o) {
        return o instanceof User u && email.equals(u.email);
    }
    // hashCode NOT overridden: uses Object's identity-based hash
}

Set<User> users = new HashSet<>();
users.add(new User("a@example.com"));
users.contains(new User("a@example.com")); // almost certainly false
users.add(new User("a@example.com"));
users.size();                              // usually 2: "duplicates" in a Set

In test code this shows up as deduplication that does not deduplicate, assertTrue(list.contains(expected)) failing even though the objects look identical, and Map lookups of expected results returning null.

A correct implementation

import java.util.Objects;

final class User {
    private final String email;
    private final String role;

    User(String email, String role) { this.email = email; this.role = role; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User other)) return false;
        return Objects.equals(email, other.email) && Objects.equals(role, other.role);
    }

    @Override
    public int hashCode() {
        return Objects.hash(email, role);
    }
}

Use the same fields in both methods. Objects.equals handles nulls.

Easiest in Java 16+: records

record User(String email, String role) {}

new User("a@example.com", "admin").equals(new User("a@example.com", "admin")); // true

Records generate equals, hashCode and toString from their components. They are ideal for test data and expected-result objects. Lombok’s @EqualsAndHashCode or @Value, or IDE generation, are alternatives in older codebases.

Other pitfalls

  • Mutable keys: if a field used in hashCode changes after the object is put in a HashSet, the object is stranded in the wrong bucket. Keep keys immutable.
  • Inheritance and symmetry: using getClass() != o.getClass() versus instanceof matters when subclasses add fields. instanceof with a subclass that adds state can break symmetry, which is one reason to make value classes final.
  • JPA entities: equality based on a generated ID that is null before persisting causes subtle bugs; that is a separate, well-known debate.

What interviewers look for

  • The exact direction of the rule: equal implies same hash, not vice versa.
  • The bucket-then-equals mechanism.
  • A concrete failure with HashSet or HashMap.
  • Modern solutions: records, Objects.hash, and avoiding mutable keys.

A strong answer covers

  • Equal objects must have equal hash codes; the reverse is not required
  • equals must be reflexive, symmetric, transitive, consistent, and false for null
  • Explains how HashMap/HashSet use hashCode then equals
  • Shows the failure when only equals is overridden
  • Uses records, Objects.equals/Objects.hash, or IDE/Lombok generation; avoids mutable keys
Practise answering under time pressure

Type to search across SDETRole.

Full-text search by Pagefind · runs in your browser Open search page →