Using equals(Object obj) and hashCode() methods

Using equals(Object obj) and hashCode() methods

Java super class “java.lang.Object” provides two important methods for comparing objects equals(Object obj) and hashCode(). These methods are useful when there is a need of implementation for comparing objects. this article describes How these methods are used together, Their default implementations that java provides, and The situations where developers have to provide custom implementation.

equals(Object obj)

This method is used to compare two objects from memory location. It returns true if the object passed in the method exist on the same memory location where the current object exist.

hashCode()

hashCode() method is used to return integer representation of the object from memory address. It returns unique random integer for each instance and integer value won’t remain same for several execution of application.

Using them together

As per the Java recommendation, developers should override both methods for achieving a fully working equality mechanism — it’s not enough to just implement the equals(Object obj) method or just hashCode() method.

If two objects are equal according to the equals(Object obj) method, then calling the hashcode() method on each of the two objects must produce the same integer result.

Example

public class Student {
    private int id;
    private String name;
    public Student(int id, String name) {
        this.name = name;
        this.id = id;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

For testing purposes, we create a test class TestEqualsHashCode that compare two instances of Student the exact same attributes which are considered as equals.

public class TestEqualsHashCode{
    public static void main(String[] args) {
        Student std1 = new Student(1, "Aqib");
        Student std2 = new Student(1, "Aqib");
        System.out.println("std1 hashcode = " + std1.hashCode());
        System.out.println("std2 hashcode = " + std2.hashCode());
        System.out.println("std1 and std2 = " + std1.equals(std2));
    }
}

Output should be:

std1 hashcode = 1425235647
std2 hashcode = 2452465478
std1 and std2 = false

Now these two objects have exactly the same values but reason for returning false is that both objects are stored on the different locations in memory and producing different hash code for each instance which is the default implementation provided by Java.

Now from business perspective, if it is required that the students object should be same if they have a same student id so it would require to have some custom implementation for the equals(Object obj) method by overriding it.

Overriding equals(Object obj)

@Override
public boolean equals(Object obj) {
    if (obj == null) 
        return false;
    if (!(obj instanceof Student))
        return false;
    if (obj == this)
        return true;
    return this.getId() == ((Student) obj).getId();
}

Now if we try to run the above test class the equals(Object obj) would return true as this method is now overridden in Student class which would now behave according to custom implementation but still there is nothing change with the hashCode() method as it still behaves with these instances as different objects, it is now required to have custom implementation for the hashCode() method as well but see in below examples how we can achieve this.

Overriding hashcode()

As we override equals(Object obj) and we get the expected behavior but still the hash code of the objects are different. So, what’s the purpose of overriding hashcode()?

Now already mentioned that hashCode() is widely implemented on HashSet, HashMap, HashTable etc. Now consider a scenario that we want to store all Students objects in HashSet so we now update our TestEqualsHashCode Class ,

public class TestEqualsHashCode {
    public static void main(String[] args) {
        Student std1 = new Student(1, "Aqib");
        Student std2 = new Student(1, "Aqib");
        HashSet < Student > students = new HashSet < Student > ();
        students.add(std1);
        students.add(std2);
        System.out.println("size = " + students.size());
        System.out.println("Contains Aqib = " + students.contains(new Student(1, "Aqib")));
    }
}

Now if we run and test we get the output:

size = 2
Contains Aqib = false

Now when HashSet searches for an element inside it, it first generates the element’s hash code and looks for a bucket which corresponds to this hash code.

Here comes the importance of overriding hashcode()so let’s override it in Student and set it to be equal to the ID so that students who have the same ID are stored in the same bucket

@Override
public int hashCode() {
    return id;
}

Now Run the test class and see the output

size = 2
Contains Aqib = true

See the magic of hashcode()! The two elements are now considered as equal and stored in the same memory bucket, so any time you call contains() and pass a student object holding the same hash code, the set will be able to find the element.

The same is applied for HashMap, HashTable, or any data structure that uses a hashing mechanism for storing elements.

Hope the articles helps you understand the difference and usage of these two very useful methods of Java to work with objects and memory allocation of objects.

Best Regards,

Muhammad Aqib Arif

Recommended insights

Learn more about how we add value!

Start a Project

X

  • WORK WITH US

    Before we start, please fill this form to provide us some basic information.

  • This field is for validation purposes and should be left unchanged.
X

Oops! We could not locate your form.