Categories
Software

Understanding equals and hashcode in Java

In Java, Object class has two identity methods called equals and hashcode. We usually override them in our classes to achieve equality. Understanding the underlying mechanism of equals and hashcode can be crucial in some cases. Let’s take a brief look at what they are used for.

  • equals : compares two objects in order to determine if they are equal or not. If you do not override this method or no parent class overrides it, equals method acts like == operator by default. (Beware that == operator compares object references not values.)
  • hashcode: provides and an int value which is derived from the memory address of the object. If two objects occupy same memory address they have same hashcode by default.

If you leave equals and hashcode methods as it is, one can conclude that whenever equals is true for two object, their hashcode value must be same.

So in order to not to break this rule, if you override one of these methods, you must override both.

We have equals, why do we need hashcode ?

In java, objects can not be used as an index in arrays. But objects can be used as a key in Hash-based collection containers like HashMap, HashSet etc. In this regard, hashcode is used for comparing object keys in such hash-based containers.

One may be think why equals method is not used to compare objects keys. The reason is that, comparing objects using hashcode is computationally more efficient than equals method.

Do we need to override hashcode ?

No, unless you use immutable objects as a key in such hash-based containers. Another words, if you don’t override hashcode method, you have to use exact same object in order to retrieve the corresponding value.

In particular, if two objects are equal according to the equals() method, they must have the same hashCode() value (although the reverse is not generally true).

Let’s try an example: Supposed that we have a simple dog object and whenever we see dog with a name “Max”, we want them to be the same dog. Although they occupy different memory locations.


public class Dog {
    private String name;

    public Dog(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Lets make a simple test to check if new Dogs with a name of “Max” are equal to each other, then check if they can be used as a key in order to retrieve same object from a hashMap.


public static void main(String[] args) {
    System.out.println((new Dog("Max")).equals(new Dog("Max")));
    Map<Dog,String> dogMap = new HashMap<Dog,String>();
    dogMap.put(new Dog("Max"),(new Dog("Max")).getName());
    System.out.println(dogMap.containsKey(new Dog("Max")));
}

Result without overriding equals and hashcode.


false
false

As you can see, when we leave equals and hashCode methods as they are, our test failed.

Lets override equals method and run the test again.


public class Dog {
private String name;

public Dog(String name) { this.name = name; } public String getName() { return name;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dog dog = (Dog) o;
return name.equals(dog.name);
} }

Result after overriding equals.


true
false

This time our test partially succeeded. Both new Dog objects with a name of Max became equal. But they still cannot be used as a key in a hashMap.
This time let’s override hashCode() method too.


public class Dog {
private String name;

public Dog(String name) {
this.name = name;
}

public String getName() {
return name;
}

@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dog dog = (Dog) o; return name.equals(dog.name);
}

@Override public int hashCode() {
return name.hashCode();
}
}

Result after overriding equals and hashcode.


true
true

Finally we got the expected result. After overriding equals and hashCode methods, every new Dog object with a name of Max become equal and even can be used as a key in hashMaps.

As you can see, overriding equals() and hashCode() object methods can be very useful in different circumstances and may increase your code clarity. Lastly, do not forget, when you override either equals() or hashCode method, you must override both.