Java - equals() and hashCode()
In Java, equals() and hashCode() are two closely related methods defined in Object. Overriding them correctly is essential whenever objects are used as keys in HashMap/HashSet or compared for logical equality.
Contents
The equals–hashCode Contract
Java's contract (documented in Object
- If
is true, thena.equals(b) must also be true.a.hashCode() == b.hashCode() - If
, it does not meana.hashCode() == b.hashCode() (hash collisions are allowed).a.equals(b) must be reflexive, symmetric, transitive, consistent, and handleequals() gracefully.null
HashMapHashSetDefault Behavior (Object class)
By default, Object.equals()Object.hashCode()
String a = new String("hello");
String b = new String("hello");
System.out.println(a == b); // false (different objects)
System.out.println(a.equals(b)); // true (String overrides equals)
System.out.println(a.hashCode() == b.hashCode()); // true (String overrides hashCode)
StringInteger
Overriding equals()
Follow this canonical pattern:
public class Employee {
private int id;
private String name;
// Constructor, getters omitted for brevity
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // same reference
if (obj == null) return false; // null check
if (getClass() != obj.getClass()) return false; // type check
Employee other = (Employee) obj;
return this.id == other.id
&& Objects.equals(this.name, other.name);
}
}
Key steps:
- Return
immediately iftrue .this == obj - Return
forfalse or different class.null - Cast and compare each significant field. Use
for nullable fields to avoid NPE.Objects.equals()
Overriding hashCode()
A good hash code should distribute objects uniformly. The easiest approach is Objects.hash()
@Override
public int hashCode() {
return Objects.hash(id, name); // combine significant fields
}
Manual implementation using the prime-multiplier pattern:
@Override
public int hashCode() {
int result = 17; // non-zero constant
result = 31 * result + id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
hashCode()equals()HashMapHow HashMap Uses Both Methods
When you call map.put(key, value)
- HashMap calls
to determine the bucket index.key.hashCode() - Inside the bucket, it calls
to find a match or insert a new entry.key.equals(existingKey)
Map<Employee, String> map = new HashMap<>();
Employee e1 = new Employee(1, "Alice");
Employee e2 = new Employee(1, "Alice"); // logically same as e1
map.put(e1, "Engineering");
// Without overriding equals/hashCode: e2 lookup FAILS (different hash)
// With correct overrides: e2 lookup SUCCEEDS
System.out.println(map.get(e2)); // "Engineering" — if overrides are correct
If hashCode()e1e2get()null
Common Pitfalls
- Overriding only equals: objects are logically equal but have different hash codes; they won't be found in collections.
- Mutable key fields: mutating a field used in
after inserting into ahashCode() changes its bucket; the entry becomes unreachable.HashMap - Using
in equals: can break symmetry in class hierarchies; preferinstanceof unless designing for polymorphism.getClass() - Not calling
in inheritance: only do so if the parent's fields should also be part of equality.super.hashCode()
Note:
Modern IDEs (IntelliJ, Eclipse) and libraries like Lombok