Every class in Java inherits from the Object class. This means every class gets default implementations of several methods, including equals() and hashCode(), that are available to every class you create.
These methods serve specific, crucial roles, especially when customizing their behavior through overrides. Let's dive into why and how you would override them with updated examples and guidance.
public class Person {
@Override
public boolean equals(Object obj) {
// Custom logic here
}
@Override
public int hashCode() {
// Custom logic here
}
}
Key Takeaways
- The
equals()method checks if two objects are equivalent based on logical equality, not just their memory addresses. - The
hashCode()method provides a hash code for objects, critical for hash-based collections. - Override both methods when creating value objects to maintain correctness in hash collections like
HashMap. - The contracts for
equals()andhashCode()must be carefully maintained to ensure consistent behavior. - A custom
hashCode()is essential ifequals()is overridden for consistent key storage in hash collections.
Java equals()
The equals() method in Java compares the logical equality of two objects. The default implementation in the Object class compares object references, meaning it checks if two references point to the same memory address:
public boolean equals(Object obj) {
return (this == obj);
}
For many practical applications, especially in business logic, this default is insufficient.
For example:
Payment payment1 = new Payment("USD", 50.00);
Payment payment2 = new Payment("USD", 50.00);
payment1.equals(payment2); // returns false by default
To make two Payment instances with identical values equal, override equals():
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Payment payment = (Payment) obj;
return Double.compare(payment.amount, amount) == 0 &&
Objects.equals(currency, payment.currency);
}
The equals contract
The equals() method must adhere to a contract that enforces the following:
- Reflexive: For any non-null reference value x, x.equals(x) should return true.
- Symmetric: For any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) is true.
- Transitive: For any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- Consistent: Multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons is modified.
- For any non-null reference value x, x.equals(null) should return false.
Maintaining this contract avoids subtle bugs in your application logic and is crucial when integrating with complex frameworks and libraries.
Java hashCode()
The hashCode() method returns an integer hash code, which is particularly important when using hash-based collections like HashMap, HashSet, and HashTable.
These collections use hash codes to organize their data efficiently:
HashMap map = new HashMap<>();
map.put(new Payment("USD", 50.00), "Sam");
String result = map.get(new Payment("USD", 50.00)); // should return "Sam"
To ensure correct functionality, the hashCode() method must return consistent IDs for "equal" objects.
The hashCode contract
The hashCode() method contract specifies:
- If two objects are equal per the
equals()method, they must have the same hash code. - If two objects are unequal, they are not required to have distinct hash codes, but distinct hash codes may improve hash table performance.
- The hash code must remain consistent through an object’s life unless the object changes in a way that affects equals comparisons.
Here's a revised hash code method for the Payment class:
@Override
public int hashCode() {
return Objects.hash(currency, amount);
}
Why you need to override hashCode() and equals()
When you override equals(), you must also override hashCode(). This is because both methods must honor their contracts consistently for class instances to behave predictably in hash-based collections.
Overriding both ensures that:
HashMap map = new HashMap<>();
map.put(new Payment("USD", 43.00), "Sam");
String expected = map.get(new Payment("USD", 43.00)); // returns "Sam", not null
Consequences of not overriding hashCode()
If you override equals() but not hashCode(), you risk incorrect behavior in hash-based collections:
HashMap map = new HashMap<>();
map.put(new Payment("USD", 43.00), "Sam");
String result = map.get(new Payment("USD", 43.00)); // returns null due to default hash code
This can cause logical errors in applications, as different instances with equal values might map to separate buckets due to their individual hash codes.
Java equals() example
Here’s a typical example overriding equals():
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Payment payment = (Payment) obj;
return Double.compare(payment.amount, amount) == 0 &&
Objects.equals(currency, payment.currency);
}
Ensure the equals() logic is consistent with its contract, covering null checks and class compatibility.
Java hashCode() example
An example of overriding hashCode() could be:
@Override
public int hashCode() {
return Objects.hash(currency, amount);
}
Leverage the Objects.hash() utility for concise, effective hash code generation that reflects the same fields used in equals().
FAQ
Why isn’t my get() call on a HashMap returning the expected value?
Ensure you’ve overridden both equals() and hashCode() in any key class. If they aren’t aligned, it can lead to mismatched hash buckets and unexpected results.
Can objects with different hash codes be equal?
No, if two objects are equal (according to equals()), they must have the same hash code.
What happens if you don’t override equals() or hashCode()?
Your objects will fall back to the default reference-based behavior, which could lead to unexpected bugs, especially in collections requiring logical equality comparison.
Is overriding hashCode() essential if not using hash-based collections?
Not strictly, but it’s considered good practice to ensure any potential hash-based collection usage will function correctly.
