Of course! This is a fundamental and crucial topic in Java. Understanding equals() and hashCode() is essential for writing correct, robust, and efficient Java applications.

Let's break it down.
The Golden Rule: The Contract
The entire concept revolves around a contract defined in the Object class. If you override one, you almost always need to override the other.
The contract states:
If two objects are equal according to the
equals()method, then they must have the samehashCode()value.(图片来源网络,侵删)
Important: The reverse is not true. Two objects can have the same hashCode() but not be equal. This is called a hash collision.
Analogy: The Library System
equals(): This is like checking if two books are the exact same edition (same title, same author, same ISBN). It's a deep comparison.hashCode(): This is like the book's location in the library (e.g., "Aisle 3, Shelf 2, Bin 15"). It's a quick way to find a general area.- The Contract: If two books are the exact same edition (they are
equals), they must be in the exact same location (they have the samehashCode). - Collision: Two different books (e.g., "Java for Beginners" and "Python for Beginners") could, by chance, be assigned to the same location ("Aisle 3, Shelf 2, Bin 15"). The librarian would then use
equals()to check which one you actually want.
The equals() Method
What is it for?
The default equals() method in the Object class behaves like the operator: it checks if two references point to the exact same object in memory.
For most objects, this isn't what we want. We want to check if two objects have the same content or value. For example, two different Person objects with the same name and birth date should be considered "equal".

How to Override equals() Correctly (The 7 Rules)
When you override equals(), you must follow this canonical procedure:
- Check for
null: If the other object isnull, they can't be equal. - Check for
thisreference: If the other object is the same instance asthis, they are obviously equal. - Check for class type: If the other object is not an instance of the same class, they are not equal. (Using
instanceofis better thangetClass() == obj.getClass()for subclasses). - Cast the object: Now you can safely cast the object to your specific class.
- Compare significant fields: Compare the fields that define the object's state. Use
Objects.equals()for object fields to handlenullsafely, and for primitive fields. - Return
trueif all significant fields are equal. - Return
falseotherwise.
Example: Person Class
import java.util.Objects;
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 1. Override equals()
@Override
public boolean equals(Object o) {
// 1. Check for null
if (o == null) {
return false;
}
// 2. Check for this reference
if (this == o) {
return true;
}
// 3. Check for class type
if (!(o instanceof Person)) {
return false;
}
// 4. Cast the object
Person person = (Person) o;
// 5. Compare significant fields
// Use Objects.equals() for String fields to handle nulls correctly.
// Use == for primitive int fields.
return age == person.age &&
Objects.equals(name, person.name);
}
// Getters, constructor, toString()...
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
The hashCode() Method
What is it for?
hashCode() returns an integer, called a hash code, which is used by data structures like HashMap, HashSet, and Hashtable to store and retrieve objects efficiently.
When you put an object into a HashMap, it:
- Calculates the object's
hashCode()to find the "bucket" (or location) where it should be stored. - If the bucket is empty, it stores the object there.
- If the bucket is not empty (a collision), it uses
equals()to compare the new object with the existing ones in that bucket to find the right spot or determine if the key is already present.
A good hashCode() function distributes objects across many buckets to minimize collisions and keep lookups fast (O(1) on average).
How to Override hashCode() Correctly
The goal is to produce a hash code that is consistent and based on the same fields you used in equals().
- Choose significant fields: Use the same fields that you used to determine equality in
equals(). - Initialize a result: Start with a non-zero constant, like
result = 17. - Combine field hashes: For each significant field, update the result using a formula. A common and effective formula is:
result = 31 * result + (field == null ? 0 : field.hashCode());- Why 31? It's a prime number. Multiplying by a prime number helps reduce collisions. It's also an odd number, which helps ensure that multiplication and bit shifting have good properties.
- Return the result.
Example: Person Class (continued)
public class Person {
// ... (previous code) ...
// 2. Override hashCode()
@Override
public int hashCode() {
// 1. Choose significant fields (same as in equals)
// 2. Initialize a result
int result = 17;
// 3. Combine field hashes
// Use a prime number (like 31) as a multiplier.
result = 31 * result + (name == null ? 0 : name.hashCode());
result = 31 * result + age; // Autoboxing to Integer happens here
// 4. Return the result
return result;
}
}
The Danger: Breaking the Contract
If you override equals() but not hashCode(), or vice-versa, you will break the contract and cause serious bugs in collection classes.
Example of a Broken Contract
import java.util.HashMap;
import java.util.Map;
public class BrokenContractExample {
public static void main(String[] args) {
Map<Person, String> personMap = new HashMap<>();
Person p1 = new Person("Alice", 30);
Person p2 = new Person("Alice", 30);
// p1 and p2 are equal according to our equals() method
System.out.println("p1.equals(p2): " + p1.equals(p2)); // true
// Let's put p1 in the map
personMap.put(p1, "Engineer");
// Now, let's try to get the value using p2
// Since p1 and p2 are equal, we should get "Engineer" back.
// But we won't, because we broke the hashCode contract!
String job = personMap.get(p2);
System.out.println("Job for p2: " + job); // Prints null! BUG!
}
}
// Assume the Person class above has an equals() but NO hashCode() override.
// The default hashCode() from Object is used, which is based on memory address.
// p1.hashCode() != p2.hashCode(), so the HashMap looks in the wrong bucket.
The Modern Way: Objects.hash()
Since Java 7, the java.util.Objects class provides a convenient utility method to generate hash codes. It simplifies the process and reduces the chance of error.
How it works: Objects.hash(Object... values) takes a list of fields and computes the hash code for you using the same prime-number logic.
Example: Person Class using Objects.hash()
import java.util.Objects;
public class Person {
private final String name;
private final int age;
// ... constructor, getters, equals(), toString() ...
// The modern, clean way to implement hashCode()
@Override
public int hashCode() {
// This single line does all the work!
return Objects.hash(name, age);
}
}
This is the recommended approach for most cases. It's concise, readable, and less error-prone.
Summary and Best Practices
| Method | Purpose | Key Rules | Best Practice |
|---|---|---|---|
equals() |
Checks if two objects have the same ** |

