In Java, the methods equals() and hashCode() are extremely important for the correct operation of collections such as HashMap, HashSet, and others. This question is often overlooked by novice developers, although violating the contracts of these methods can lead to hard-to-detect bugs in application logic.
Background:
In the Java language, all classes inherit the methods equals() and hashCode() from the Object class. By default, equals() compares references to objects (i.e., their physical location in memory), while hashCode() returns a unique code for each object. However, for user-defined classes, it is often necessary to compare objects based on content rather than by reference.
Problem:
If the methods equals() and hashCode() are not overridden or are incorrectly overridden, objects may behave unexpectedly in collections that rely on hashing. This can lead to missing elements, duplication, or search errors.
Solution:
Always override both methods together, strictly following the contract:
a.equals(b) == true, then a.hashCode() == b.hashCode()a.equals(b) == false, the hashCode requirement is such that uniqueness is not mandatory.Example of a correct implementation:
public class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
Key features:
equals() method should be reflexive, symmetric, transitive, and consistent.hashCode() method should return the same value for the object when its data is unchanged.Can you use only equals() without hashCode() in classes that will be stored in HashSet?
No. If you override only equals(), hash-based collections will not correctly determine the uniqueness of objects. HashSet first compares hashCode, then equals.
Do all class fields have to be used in equals() and hashCode()?
No. Only those that are significant for the logical identity of the class. For instance, if an object has an internal unique identifier, it is sufficient to use it.
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(id, user.id); }
Can you base equals() on a getter instead of a direct field?
Usually yes, if there are no side effects and the getter is stable. But there is a risk that the getter returns different values on different calls—then behavior will be unpredictable.
hashCode() when equals() is overridden.hashCode() calculations.A developer implements a User class and only defines the equals() method, forgetting hashCode(). As a result, when adding and searching for objects in HashSet, duplicates occur and elements "disappear".
Pros:
Cons:
A developer implements both methods strictly according to the contract, using only the id within the equality and hashing logic. Collections behave as expected, and searching and storing work correctly.
Pros:
Cons: