Core Java

Java equals() and hashCode()

Introduction:

Java Object class provides basic implementation of methods – hashCode() and equals(). These methods are extremely useful especially when working with the Collection framework. The hash table implementations rely on these methods for storing and retrieving data.

In this tutorial, we’ll learn about the contract between hashCode() and equals(), their default implementations. We’ll also talk about when and how to override these methods.

Default Behaviour:

Let’s first look at the default implementations of these methods:

1. equals():

The equals() method present in the Object class simply compares the object references:

public boolean equals(Object obj) {
    return (this == obj);
}

So, by default, obj1.equals(obj2) is same as obj1 == obj2.

The equals() method compares actual values for classes like String etc as it’s overridden in those respective classes.

2. hashCode():

The hashCode() method’s signature in the JDK is:

public native int hashCode();

Here, the native keyword indicates that the method is implemented in native code using JNI (Java Native Interface).

The hashCode() method returns an int type. The returned value, by default, represents the object memory address.

Implementation Principles:

Before we override the equals() and hashCode() methods, let’s first look at the guidelines:

1. equals(): Our implementation of equals() method must be:

  • Reflexive: for any reference value obj, obj.equals(obj) should return true
  • Symmetric: for the reference values obj1 and obj2, if obj1.equals(obj2) is true then obj2.equals(obj2) should also return true
  • Transitive: for the reference values obj1, obj2, and obj3, if obj1.equals(obj2) is true and obj2.equals(obj3) is true then obj1.equals(obj3) should also return true
  • Consistent: Provided we haven’t changed the implementation, multiple invocations of the equals() method must always return the same value

2. hashCode(): When implementing hashCode(), we must consider the following points:

  • In a single execution, multiple invocations of hashCode() must return the same value, provided we don’t change a property in the equals() implementation
  • the objects that are equal must return the same hashCode() value
  • the two or more unequal objects can have the same hashCode() value

equals() and hashCode() Contract:

Although all the above principles are to be kept in mind while overriding these methods, there’s one popular rule among these:

For the two objects obj1 and obj2,

  • If obj1.equals(obj2) then obj1.hashCode() = obj2.hashCode() must hold true
  • However, if obj1.hashCode() == obj2.hashCode(), then obj1.equals(obj2) can either return true or false i.e. obj1 and obj2 might or might not be equal

This is popularly known as the equals() and hashCode() contract. 

Why Override equals() And hashCode()?

The hashCode() and equals() method play an important role in storing and retrieving elements in a hash table based implementation. The hashCode() determines the bucket the given item maps to. Within a bucket, the equals() method is used to look for the given entry.

Let’s say we have an Employee class:

public class Employee {

    private int id;
    private String name;

    //constructors, getters, setters, toString implementations

}

And a HashMap storing Employee as the keys:

Map<Employee, Integer> map = new HashMap<>();

map.put(new Employee(1, "Sam"), 1);
map.put(new Employee(2, "Sierra"), 2);

Now that we have inserted two entries, let’s try a containsKey() check:

boolean containsSam = map.containsKey(new Employee(1, "Sam")); //false

Although we have an entry for Sam, containsKey() returned false. That’s because we haven’t yet overridden the equals() and hashCode() methods. And by default, equals() will simply do a reference-based comparison.

Overriding equals() And hashCode():

As per Javadocs:

When we override equals() method, we must also override the hashCode() method

This will help to avoid breaking the equals-hashCode contract.

Note that the compiler won’t complain if we break the contract, but we might end up facing unexpected behaviors say when we store such objects as keys in a HashMap.

We can quickly override these methods using the feature of an IDE. When using Eclipse, we can go to Source->Generate hashCode() and equals(). Let’s look at the generated implementations for our Employee class:

public class Employee {

    ...
     
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } 
        else if(!name.equals(other.name))
            return false;

        return true;
    }
}

Clearly, the same fields have been used for implementing both equals() and hashCode() methods to keep up with the contract.

Best Practices:

Some of the best practices to follow when working with equals() and hashCode() includes:

  • Implement hashCode() to evenly distribute items across various buckets. The idea is to minimize the number of collisions and in turn have a good performance
  • We should use same fields for both equals() and hashCode() implementations
  • Prefer immutable objects as keys in a HashMap as they support caching the hash code values
  • When working with ORM tool, always use getters instead of fields in the hashCode() and equals() method definition. That’s because a few fields might be lazy loaded

Conclusion:

In this tutorial, we first looked at the default implementations of equals() and hashCode() methods. Later, we discussed when and how to override these methods.

One comment
Pingback: Java equals() and hashCode() | Hi there Android - Android World

Leave a Comment

Your email address will not be published. Required fields are marked *