🚀 Rules for Overriding hashCode() in Java
When overriding hashCode(), follow these rules to ensure correctness, efficiency, and compliance with Java collections like HashMap, HashSet, and HashTable.
1️⃣ Follow the equals()–hashCode() Contract
The most important rule:
If two objects are equal (
a.equals(b) == true), thena.hashCode() == b.hashCode()must also betrue.However, if two objects are not equal, their hash codes may or may not be different.
Why is this important?
Hash-based collections (HashMap, HashSet) use hashCode() to group objects into buckets before checking equals(). If this contract is violated, data retrieval may fail.
❌ Bad Example: Violating the Contract
class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
// ❌ Violates the contract (Different hashCodes for equal objects)
@Override
public int hashCode() {
return (int) (Math.random() * 1000); // Bad! Produces random values
}
}
📌 Problem: Equal Person objects (equals() returns true) may get different hash codes, breaking HashSet and HashMap functionality.
✅ Fixed Version
@Override
public int hashCode() {
return Objects.hash(name); // Ensures same hash for equal objects
}
2️⃣ Use the Same Fields in hashCode() as in equals()
When overriding hashCode(), include only the fields used in equals(). This ensures consistent hashing.
✅ Correct Example
import java.util.Objects;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // ✅ Uses same fields as equals()
}
}
📌 Why is this correct?
equals()comparesnameandage, sohashCode()must use the same fields.- Ensures equal objects have the same hash code.
3️⃣ Ensure Consistency: hashCode() Must Always Return the Same Value for the Same Object
If an object does not change,
hashCode()must return the same value every time it is called.
❌ Bad Example
@Override
public int hashCode() {
return (int) (System.nanoTime() % 1000); // ❌ Changes on every call!
}
📌 Problem: hashCode() changes constantly, breaking HashSet and HashMap operations.
✅ Fixed Version
@Override
public int hashCode() {
return Objects.hash(name, age);
}
4️⃣ Avoid Using Mutable Fields in hashCode()
If a field used in
hashCode()changes after an object is inserted into aHashMap, it may be lost!
❌ Bad Example: Using a Mutable Field in hashCode()
class Person {
private String name;
private int age;
private double salary; // ❌ Mutable field
@Override
public int hashCode() {
return Objects.hash(name, age, salary); // ❌ Salary can change!
}
}
📌 Problem:
- If
salarychanges after adding the object to aHashMap,hashCode()will change. - The object may not be found when trying to retrieve it.
✅ Fixed Version
@Override
public int hashCode() {
return Objects.hash(name, age); // ✅ Excludes mutable field
}
📌 Solution: Only use immutable fields in hashCode().
5️⃣ Use Objects.hash() or a Prime Number Formula
✅ Preferred: Objects.hash() (Simple & Safe)
@Override
public int hashCode() {
return Objects.hash(name, age, email);
}
📌 Why?
- Handles
nullvalues safely. - Generates consistent hash codes.
✅ Alternative: Prime Number Multiplication
@Override
public int hashCode() {
int result = 17; // Start with a prime number
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
📌 Why 31?
31is a prime number, reducing hash collisions.
6️⃣ When Overriding hashCode(), Always Override equals()
Overriding one without the other can break
HashMapandHashSetbehavior.
❌ Bad Example: Overriding hashCode() Without equals()
@Override
public int hashCode() {
return Objects.hash(name);
}
📌 Problem:
- If
equals()is not overridden, objects will be compared using==instead of their content.
✅ Fixed Version: Override Both
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
📌 Ensures equals() and hashCode() are consistent for collections like HashMap.
📌 Summary: Rules for Overriding hashCode()
| Rule | Why It Matters |
|---|---|
1. If equals() returns true, hashCode() must return the same value. | Ensures objects in HashSet and HashMap work correctly. |
2. Use the same fields in hashCode() as in equals(). | Guarantees consistency. |
3. Ensure hashCode() always returns the same value for the same object. | Prevents unexpected behavior in collections. |
4. Avoid using mutable fields in hashCode(). | Ensures objects can be found after being inserted into a HashMap. |
5. Use Objects.hash() or prime numbers for hashing. | Creates a strong, consistent hash function. |
6. Always override equals() when overriding hashCode(). | Ensures logical equality and proper collection behavior. |
✅ Final Best Practices
✔ Always override hashCode() when overriding equals().
✔ Use Objects.hash() for simplicity and correctness.
✔ Only use immutable fields in hashCode().
✔ Test with HashMap and HashSet to verify correctness.
By following these rules, your Java objects will work correctly, efficiently, and predictably in hash-based collections! 🚀