hashcode() and equals() contract in Java
In last article, we saw the implementation of Hashtable. Before moving on to HashMap and its implementation let’s get to know about two primary and most used methods by java developer: equals() and hashCode(). These methods are also underlying core for both Hashtable and HashMap.
public boolean equals(Object obj)
public int hashCode()
Both methods are part of [Object class](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html)
in java. This is the root of the class ==hierarchy==. Every class has Object class
as a superclass. All objects, including arrays, implement the methods of this class.
As per Javadoc equals()
method indicates whether some other object is “equal to” given object. The default implementation of this method provided by Object class checks if two object references point to the same memory location, this comparison is known as a shallow comparison. It is called shallow comparison because the class doesn’t have any data members thus, comparison only happens on given objects references. The sub-class have their own implementation of this method as they need data member or state of the object also to be compared, such comparison is known as deep comparison.
equals()
method implements an equivalence relation i.e reflexive, symmetric, transitive, consistent (refer Javadoc for these properties elaboration). One important point about this function is that whenever any object is compared against null
it always returns false and not NullPointerException.
As per Javadoc hashcode()
method returns a hash code value as an integer for the object. Hashcode value is mostly used in hashing based collections like Hashtable, HashMap, HashSet, etc. Whenever this method is invoked on the same object it should always return in the same value of integer unless the state of the object has changed or it might not be same between multiple executions of the application.
Let’s talk about the equals() and hashCode() contract:
If two objects are depicted equal by equals() method then there hashcode must be same.
If two objects are not equal by equals() method then there hashcode could be same or different.
So in simple terms, the contract is that if
obj1.equals(obj2)
thenobj1.hashCode() == obj2.hashCode()
So, it is generally necessary to override the hashCode()
method whenever equals()
method is overridden, so as to maintain the above contract. Let’s see how to override these methods. Few points to note before overriding these methods respectively:
- Do
this
(i.e current object reference) check: if yes then return true. - Do
null
check: if yes then return false. - Do
getClass()
check: verify the class of object on which method is invoked is the same as that of being compared to.instanceof
can also be used for immutable class but for others it can’t be used as it returns true if the object is an instance of a subclass. - Do not typecast the object with which comparison is being done: note the sequence
instanceof
check must be done prior to casting object. Always remember overriding and overloading are not the same. So as object class definespublic boolean equals(Object o)
don’t change the arg to your own class type e.g:public boolean equals(Test t)
. - Compare attributes of the object starting with numeric attribute because comparing numeric attribute is fast and use logical operators for combining checks. If the first field does not match, return false. It’s also worth to remember doing
null
check on individual attributes before callingequals()
method on them recursively to avoid NullPointerException during the check. - Last but not least, do not forget to override the
hashCode()
method ;)
Points to remember while overriding hashcode()
method:
- Do check
null
: Before calling the method on members or fields to avoid NullPointerException, remember to check null if a member is null then return zero. - Do keep the return of
hashcode()
function asint
. - Do cache the hashcode for immutable objects leads to improved performance.
If you don’t override method properly your Object may not function correctly on hash-based collections, always tries to use effective hashcode implementation. IDEs like Eclipse, IntelliJ Idea provide the implementation use them as they are generally good enough to generate good hash code. For IntelliJ Idea, one can press command + N(Code -> Generate)
and choose the hashCode()
method.
Now, we know things to ponder before overriding let’s take a look at an example, in the below code I have used IntelliJ Idea to override these methods for me.
https://gist.github.com/lucifercr07/b0e1adbaed2de5fdff7b7494ad6ae7e2
Few quirks of these functions:
- Equal objects must produce the same hash code as long as they are equal, however unequal objects may not produce distinct hash codes.
- String and wrapper classes like Integer, Float and Double override
equals()
method but StringBuffer doesn’t override it. - While comparing objects always prefer
equals()
as it does deep comparison rather than == shallow comparison(behavior of Object classequals()
also, especially for String comparison useequals()
. - From Java 7 we can also use a new utility class called java.util.Objects for
null
safe equality check and calculating hashcode. e.g:Objects.equals(example1, example2);
andint exampleHashCode = Objects.hash(example1);
instanceof
check breaks symmetric property ofequals()
as,subClass instanceof ParentClass
is true butParentClass instanceof subClass
is false. Thus, the symmetric property ifa equals b
thenb equals a
is broken.
In next article, we’ll look into HashMap as promised earlier :)
Resources: