How to override hashcode() and equals() method in java

hashcode() and equals() : Overview

hashcode() and equals() methods are defined in java.lang.Object class which is the super class of all classes in java and hence may be overridden in any class.

    • The signature of hashcode() method is public int hashCode() which means it should return an integer, and
    • The signature of equals() method looks like public boolean equals(Object obj) which means it returns a boolean value indicating whether the object which called it is equal to the object which is passed as an argument to it or not.

Why Override hashcode() and equals() : Practical Scenario

Ever came across putting the objects of your own classes into a hash based collection (Hash based collections are those which use a hashcode to determine the location at which to store the objects such as a java.util.HashSet, java.util.HashMap etc.) and getting them back.

If so, then you might know that a HashSet only keeps distinct objects and over writes a previously stored object with a later added equal object. Have you thought how does HashSet know which two objects are the same and which are distinct. It is because of the hashCode() and equals() method.

hashCode() and equals() : Importance in Collections

hashCode() : Every object has a hashcode and can be considered as a unique code related to an object of a class. But hashcode of an object becomes extremely important when it comes to Collections. Here is how :

  • When inserting an object into a Collection, its hashcode determines the location in that collection where the object will be stored.
  • When retrieving the object from a Collection, again its hashcode is used to find the location where it might have been stored.

equals() : The equality of objects is checked using equals() method. Again, this method becomes extremely important when it comes to Collections. Here is how :

  • When retrieving the object from a Collection, hashcode is used to find the location where it might have been stored and after fetching, equals method is invoked on the object being fetched passing the object fetched from the collection to it as an argument.
  • equals() method checks the objects to be equal and returns the object if it finds them equal else returns null.

It might be clear now that hashCode() method invoked on an object multiple times should return the same value otherwise an object inserted into a collection could NEVER be found.

Similarly, if two objects are considered to be equal on the basis on some property, then calling equals() method on objects having same property value should return true.

Coming to the point, how to override hashCode() and equals() method in our class. Just shown below :

public class Book {
        /* Properties of class */
	String bookName;
	int serialNum;

	/*
	 * Override hashCode method to return custom hash codes
	 */
	public int hashCode() {
		int h = 0;
		// hash calculation
		int len = bookName.toCharArray().length;
		for (int i = 0; i < len; i++) {
			h = h + 31 * h;
		}
		return h;
	}

	/*
	 * Override equals method according to your equality criteria
	 */
	public boolean equals(Object o) {
		// check if both objects refer to same object, which means they are
		// equal. Here this is the object which invoked equals method
		if (this == o) {
			return true;
		}
		//check if both objects should be compared or not
		if (o instanceof Book) {
                        // cast to your class to access its fields
			Book secondObject = (Book) o;
			//check both fields are equal, means objects are equal
			if (this.bookName.equals(secondObject.bookName)
					&& this.serialNum == secondObject.serialNum) {
				return true;
			}
			return false;
		}
		return false;
	}

        public static void main(String[] args) {
		// lets use Hashmap as the collection
		Map<Book, String> map = new HashMap<Book, String>();
		// create an object for storing in map
		Book obj = new Book();
		// assign values to its variables
		obj.bookName = "coDippa";
		obj.serialNum = 1;
		// store it
		map.put(obj, "Stored an object");
                // create another object and assign the same parameters to its variables
                // in order to make it identical
		Book duplicateObject = new Book();
		duplicateObject.identifier = "coDippa";
		duplicateObject.serialNum = 1;
                // search the object and print its value. 
                // This prints "Stored an object". We found the object back!!
		System.out.println(map.get(duplicateObject));
		//change one variable and retry finding it
		duplicateObject.bookName = "new Value";
		//this prints null. The object could not be found
		System.out.println(map.get(duplicateObject));
	}
}

Detail : In the above example, we have a class named Book which has two properties, name of the book and its serial number. We create an object (with bookName as “coDippa” and serial number 1) and put it into a map. Then we create a new object with the same property values and search it in the map. We find it !!!. Then we change “bookName” property of this new object and again search it. This time it could not be found. Obviously, since the name is different.

What happens behind the scenes is :

  • When putting an object in a collection :
    1.  Its place in that collection needs to be decided, hashcode() method is used for this. Our implementation of hashCode calculates a value based on the length of bookName property.
    2. Same object is not already present in the collection, equals() method decides this. Our implementation of equals() checks the book name and its serial number to compare objects.
  • When retrieving an object from a collection :
    1. The location at which the object is stored is to be determined, hashcode() method is used for this.
    2. The object being returned is the same as requested, equals() method is again used to decide this.

Note : This post is NOT about the working of HashMap. It only shows how hashcode() and equals() method are utilized in Collections and the use and importance of overriding them.

Let’s tweak in

  1. equals() method defined in java.lang.Object class just checks the references and nothing else. It will only return true for references pointing to the same object.
  2. The signature of the overridden hashcode and equals method should exactly match with these methods defined in java.lang.Object class otherwise they will not be considered to be overridden.
  3. The type of argument of equals() method is java.lang.Object. This is because it can be overridden in any class and java.lang.Objectis the superclass of all classes.
  4. Always perform reference check (if (this == o)) at the start of equals() method. This is to prevent further processing if the objects being compared are the same objects.
  5. The better the implementation of hashCode() method, the better is the performance of the Collection since lesser number of objects are stored at the same memory location.
  6. hashCode() method which returns a constant is the poorest implementation since it will return the same location for every object.
  7. In Collections framework, equals() method is utilized where ever object comparison is required. For example, in contains(Object o) method of java.util.ArrayList, java.util.LinkedList
  8. Both equals() and hashCode() method are utilized in all Hash based Collection classes (java.util.HashSet, java.util.HashMap, java.util.TreeMap etc.) such as in put(K key, V value) method of java.util.TreeMap, java.util.HashMap, add(E e) method of java.util.HashSet.

If you are still reading means you liked the post till this point. Do not hesitate to provide your feedback / queries in the space below, specially reserved for this purpose only.

Leave a Reply