hashcode() and equals() contract in Java

Hash Code
Photo by Mari Helin on Unsplash

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 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.

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:

  1. Do this(i.e current object reference) check: if yes then return true.
  2. Do null check: if yes then return false.
  3. 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.
  4. 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 defines public boolean equals(Object o) don’t change the arg to your own class type e.g: public boolean equals(Test t).
  5. 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 calling equals() method on them recursively to avoid NullPointerException during the check.
  6. Last but not least, do not forget to override the hashCode() method ;)

Points to remember while overriding hashcode() method:

  1. 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.
  2. Do keep the return of hashcode() function as int .
  3. 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:

  1. Equal objects must produce the same hash code as long as they are equal, however unequal objects may not produce distinct hash codes.
  2. String and wrapper classes like Integer, Float and Double override equals() method but StringBuffer doesn’t override it.
  3. While comparing objects always prefer equals() as it does deep comparison rather than == shallow comparison(behavior of Object class equals() also, especially for String comparison use equals() .
  4. 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); and int exampleHashCode = Objects.hash(example1);
  5. instanceof check breaks symmetric property of equals() as, subClass instanceof ParentClass is true but ParentClass instanceof subClass is false. Thus, the symmetric property if a equals b then b equals a is broken.

Computers are all I want to know about.