equals() and hashCode()
Object comparison and hashing
Interview Relevant: Very common interview topic
7 min read
equals() and hashCode() in Java
These methods define how objects are compared and hashed. Properly implementing them is essential for using objects in collections.
The Contract
- equals(): Checks if two objects are "equal" in value
- hashCode(): Returns an integer hash for the object
- Rule: If a.equals(b), then a.hashCode() == b.hashCode()
- Note: Same hashCode doesn't mean equals (collisions allowed)
⚠️ Critical: If you override equals(), you MUST override hashCode()! Breaking this contract breaks HashSet, HashMap, etc.
🔑 Interview Question: Why override both? Because hash-based collections use hashCode() to find bucket, then equals() to find exact match.
Code Examples
Problem with default equals/hashCode
java
1// The Problem: Default behavior
2public class Person {
3 private String name;
4 private int age;
5
6 public Person(String name, int age) {
7 this.name = name;
8 this.age = age;
9 }
10}
11
12Person p1 = new Person("Alice", 25);
13Person p2 = new Person("Alice", 25);
14
15// Default equals() uses == (reference comparison)
16System.out.println(p1.equals(p2)); // false! Different objects
17System.out.println(p1 == p2); // false! Different references
18
19// This breaks collections!
20Set<Person> set = new HashSet<>();
21set.add(p1);
22set.add(p2);
23System.out.println(set.size()); // 2! But we wanted 1 unique person
24
25Map<Person, String> map = new HashMap<>();
26map.put(p1, "Engineer");
27System.out.println(map.get(p2)); // null! Can't find with "equal" keyProper equals() implementation
java
1// Proper equals() implementation
2public class Person {
3 private String name;
4 private int age;
5
6 public Person(String name, int age) {
7 this.name = name;
8 this.age = age;
9 }
10
11 @Override
12 public boolean equals(Object obj) {
13 // 1. Same reference? Return true
14 if (this == obj) return true;
15
16 // 2. Null or different class? Return false
17 if (obj == null || getClass() != obj.getClass()) return false;
18
19 // 3. Cast and compare fields
20 Person other = (Person) obj;
21 return age == other.age &&
22 Objects.equals(name, other.name); // Null-safe!
23 }
24}
25
26// equals() contract:
27// 1. Reflexive: x.equals(x) == true
28// 2. Symmetric: x.equals(y) == y.equals(x)
29// 3. Transitive: x.equals(y) && y.equals(z) → x.equals(z)
30// 4. Consistent: Multiple calls return same result
31// 5. x.equals(null) == falseProper hashCode() with equals()
java
1// Proper hashCode() implementation
2public class Person {
3 private String name;
4 private int age;
5
6 public Person(String name, int age) {
7 this.name = name;
8 this.age = age;
9 }
10
11 @Override
12 public boolean equals(Object obj) {
13 if (this == obj) return true;
14 if (obj == null || getClass() != obj.getClass()) return false;
15 Person other = (Person) obj;
16 return age == other.age && Objects.equals(name, other.name);
17 }
18
19 @Override
20 public int hashCode() {
21 // Use Objects.hash() for convenience
22 return Objects.hash(name, age);
23
24 // Or manual implementation:
25 // int result = 17;
26 // result = 31 * result + (name != null ? name.hashCode() : 0);
27 // result = 31 * result + age;
28 // return result;
29 }
30}
31
32// Now collections work correctly!
33Person p1 = new Person("Alice", 25);
34Person p2 = new Person("Alice", 25);
35
36Set<Person> set = new HashSet<>();
37set.add(p1);
38set.add(p2);
39System.out.println(set.size()); // 1! Correct!
40
41Map<Person, String> map = new HashMap<>();
42map.put(p1, "Engineer");
43System.out.println(map.get(p2)); // Engineer! Found it!What happens when contract is broken
java
1// What happens if you break the contract?
2public class BadPerson {
3 private String name;
4
5 // Overrides equals but NOT hashCode - WRONG!
6 @Override
7 public boolean equals(Object obj) {
8 if (obj instanceof BadPerson) {
9 return name.equals(((BadPerson) obj).name);
10 }
11 return false;
12 }
13 // Missing hashCode()!
14}
15
16BadPerson p1 = new BadPerson("Alice");
17BadPerson p2 = new BadPerson("Alice");
18
19System.out.println(p1.equals(p2)); // true
20
21Set<BadPerson> set = new HashSet<>();
22set.add(p1);
23set.add(p2);
24System.out.println(set.size()); // 2! WRONG! Should be 1
25
26// Why? HashMap/HashSet use hashCode() first!
27// Different hashCodes → different buckets → equals() never called
28
29// Modern Java: Use records (Java 16+) for automatic implementation
30public record PersonRecord(String name, int age) {
31 // equals(), hashCode(), toString() auto-generated!
32}Use Cases
- Using objects in HashSet
- Using objects as HashMap keys
- Removing duplicates from collections
- Finding objects in collections
- Value object comparison
- Unit testing equality
Common Mistakes to Avoid
- Overriding equals() without hashCode()
- Using instanceof instead of getClass() (inheritance issues)
- Not handling null fields
- Mutable fields in hashCode() (objects "move" in HashSet)
- Not using Objects.equals() for null-safety
- Including non-significant fields