Java equals() and hashCode()
Introduction:
Java Object class provides basic implementation of methods – hashCode() and equals(). These methods are extremely useful especially when working with the Collection framework. The hash table implementations rely on these methods for storing and retrieving data.
In this tutorial, we’ll learn about the contract between hashCode() and equals(), their default implementations. We’ll also talk about when and how to override these methods.
Default Behaviour:
Let’s first look at the default implementations of these methods:
1. equals():
The equals() method present in the Object class simply compares the object references:
public boolean equals(Object obj) { return (this == obj); }
So, by default, obj1.equals(obj2) is same as obj1 == obj2.
The equals() method compares actual values for classes like String etc as it’s overridden in those respective classes.
2. hashCode():
The hashCode() method’s signature in the JDK is:
public native int hashCode();
Here, the native keyword indicates that the method is implemented in native code using JNI (Java Native Interface).
The hashCode() method returns an int type. The returned value, by default, represents the object memory address.
Implementation Principles:
Before we override the equals() and hashCode() methods, let’s first look at the guidelines:
1. equals(): Our implementation of equals() method must be:
- Reflexive: for any reference value obj, obj.equals(obj) should return true
- Symmetric: for the reference values obj1 and obj2, if obj1.equals(obj2) is true then obj2.equals(obj2) should also return true
- Transitive: for the reference values obj1, obj2, and obj3, if obj1.equals(obj2) is true and obj2.equals(obj3) is true then obj1.equals(obj3) should also return true
- Consistent: Provided we haven’t changed the implementation, multiple invocations of the equals() method must always return the same value
2. hashCode(): When implementing hashCode(), we must consider the following points:
- In a single execution, multiple invocations of hashCode() must return the same value, provided we don’t change a property in the equals() implementation
- the objects that are equal must return the same hashCode() value
- the two or more unequal objects can have the same hashCode() value
equals() and hashCode() Contract:
Although all the above principles are to be kept in mind while overriding these methods, there’s one popular rule among these:
For the two objects obj1 and obj2,
- If obj1.equals(obj2) then obj1.hashCode() = obj2.hashCode() must hold true
- However, if obj1.hashCode() == obj2.hashCode(), then obj1.equals(obj2) can either return true or false i.e. obj1 and obj2 might or might not be equal
This is popularly known as the equals() and hashCode() contract.
Why Override equals() And hashCode()?
The hashCode() and equals() method play an important role in storing and retrieving elements in a hash table based implementation. The hashCode() determines the bucket the given item maps to. Within a bucket, the equals() method is used to look for the given entry.
Let’s say we have an Employee class:
public class Employee { private int id; private String name; //constructors, getters, setters, toString implementations }
And a HashMap storing Employee as the keys:
Map<Employee, Integer> map = new HashMap<>(); map.put(new Employee(1, "Sam"), 1); map.put(new Employee(2, "Sierra"), 2);
Now that we have inserted two entries, let’s try a containsKey() check:
boolean containsSam = map.containsKey(new Employee(1, "Sam")); //false
Although we have an entry for Sam, containsKey() returned false. That’s because we haven’t yet overridden the equals() and hashCode() methods. And by default, equals() will simply do a reference-based comparison.
Overriding equals() And hashCode():
As per Javadocs:
When we override equals() method, we must also override the hashCode() method
This will help to avoid breaking the equals-hashCode contract.
Note that the compiler won’t complain if we break the contract, but we might end up facing unexpected behaviors say when we store such objects as keys in a HashMap.
We can quickly override these methods using the feature of an IDE. When using Eclipse, we can go to Source->Generate hashCode() and equals(). Let’s look at the generated implementations for our Employee class:
public class Employee { ... @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (Employee) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if(!name.equals(other.name)) return false; return true; } }
Clearly, the same fields have been used for implementing both equals() and hashCode() methods to keep up with the contract.
Best Practices:
Some of the best practices to follow when working with equals() and hashCode() includes:
- Implement hashCode() to evenly distribute items across various buckets. The idea is to minimize the number of collisions and in turn have a good performance
- We should use same fields for both equals() and hashCode() implementations
- Prefer immutable objects as keys in a HashMap as they support caching the hash code values
- When working with ORM tool, always use getters instead of fields in the hashCode() and equals() method definition. That’s because a few fields might be lazy loaded
Conclusion:
In this tutorial, we first looked at the default implementations of equals() and hashCode() methods. Later, we discussed when and how to override these methods.
Be the First to comment.
Published on Java Code Geeks with permission by Shubhra Srivastava, partner at our JCG program. See the original article here: Java equals() and hashCode() Opinions expressed by Java Code Geeks contributors are their own. |
Prefer immutable objects as keys in a HashMap as they support caching the hash code values
This should be
Use only immutable objects ... or expect unexpected behavior
.It’s not a best practice it’s common sense.
Yes, I agree. Could have framed the sentence better.
Thanks for your feedback!