Java hashCode() and equals() Methods

Learn about Java hashCode() and equals() methods, their default implementation, and how to correctly override them. Also, we will learn to implement these methods using 3rd party classes HashCodeBuilder and EqualsBuilder.

The hashCode() and equals() methods have been defined in Object class which is parent class for all java classes. For this reason, all java objects inherit a default implementation of these methods.

1. The hashCode() and equals() Methods

  • equals(Object otherObject) – verifies the equality of two objects. It’s default implementation simply checks the object references of two objects to verify their equality. By default, two objects are equal if and only if they refer to the same memory location. Most Java classes override this method to provide their own comparison logic.
  • hashcode() – returns a unique integer value for the object in runtime. By default, Integer value is derived from the memory address of the object in the heap (but it’s not mandatory). The object’s hash code is used for determining the index location when this object needs to be stored in some HashTable like data structure.

1.1. Contract between hashCode() and equals()

Overriding the the hashCode() is generally necessary whenever equals() is overridden to maintain the general contract for the hashCode() method, which states that equal objects must have equal hash codes.

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode() must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
    This integer need not remain consistent between the two executions of the same application or program.
  • If two objects are equal according to the equals() method, then calling the hashCode() on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(), then calling the hashCode() on each of the both objects must produce distinct integer results.
    However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

2. Overriding the Default Behavior

Everything works fine until we do not override any of both methods in our classes. But, sometimes, the application needs to change the default behavior of some objects.

Let us understand why we need to override equals and hashcode methods.

2.1. The Default Behavior

Let’s take an example where your application has Employee object. Let us create a minimal possible structure of Employee class:

public class Employee
{
	private Integer id;
	private String firstname;
	private String lastName;
	private String department;

	//Setters and Getters
}

Above Employee class has some fundamental attributes and their accessor methods. Now consider a simple situation where you need to compare two Employee objects. Both employee objects have the same id.

public class EqualsTest {
	public static void main(String[] args) {
		Employee e1 = new Employee();
		Employee e2 = new Employee();

		e1.setId(100);
		e2.setId(100);

		System.out.println(e1.equals(e2));	//false
	}
}

No prize for guessing. The above method will print “false.”

But is it correct after knowing that both objects represent the same employee? In a real-time application, this should return true.

2.2. Overriding only equals()

To achieve correct application behavior, we need to override equals() method as below:

public boolean equals(Object o) {
	if(o == null)
	{
		return false;
	}
	if (o == this)
	{
		return true;
	}
	if (getClass() != o.getClass())
	{
		return false;
	}

	Employee e = (Employee) o;
	return (this.getId() == e.getId());
}

Add this method to the Employee class, and EqualsTest will start returning "true".

So are we done? Not yet. Let’s test the above-modified Employee class again in a different way.

import java.util.HashSet;
import java.util.Set;

public class EqualsTest
{
	public static void main(String[] args)
	{
		Employee e1 = new Employee();
		Employee e2 = new Employee();

		e1.setId(100);
		e2.setId(100);

		//Prints 'true'
		System.out.println(e1.equals(e2));

		Set<Employee> employees = new HashSet<Employee>();
		employees.add(e1);
		employees.add(e2);

		System.out.println(employees);	//Prints two objects
	}
}

The above example prints two objects in the second print statement.

If both employee objects have been equal, in a Set which stores unique objects, there must be only one instance inside HashSet because both objects refer to the same employee. What is it we are missing??

2.3. Overriding hashCode() is Also Necessary

We are missing the second important method hashCode(). As java docs say, if we override equals() then we must override hashCode(). So let’s add another method in our Employee class.

@Override
public int hashCode()
{
	final int PRIME = 31;
	int result = 1;
	result = PRIME * result + getId();
	return result;
}

Once the above method is added in Employee class, the second statement starts printing only a single object in the second statement and thus validating the true equality of e1 and e2.

3. EqualsBuilder and HashCodeBuilder

Apache commons provide two excellent utility classes HashCodeBuilder and EqualsBuilder for generating hash code and equals methods.

We can use these classes in the following manner.

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee
{
	private Integer id;
	private String firstname;
	private String lastName;
	private String department;

	//Setters and Getters

	@Override
	public int hashCode()
	{
		final int PRIME = 31;
		return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).toHashCode();
	}

	@Override
	public boolean equals(Object o) {
	if (o == null)
	   return false;

	if (o == this)
	   return true;

	if (o.getClass() != getClass())
	   return false;

	Employee e = (Employee) o;

	return new EqualsBuilder().
			  append(getId(), e.getId()).
			  isEquals();
	}
}

4. Generating hashCode() and equals() in Eclipse IDE

Most editors provide common source code templates. For example, Eclipse IDE has the option to generate an excellent implementation of hashCode() and equals().

Right click on Java file -> Source -> Generate hashCode() and equals() …

Generate HashCode and Equals In Eclipse
Generate hashCode() and equals() In Eclipse

5. Best Practices to Follow

  1. Always use the same fields to generate hashCode() and equals(). As in our case, we have used employee id.
  2. The equals() must be consistent (if the objects are not modified, then it must keep returning the same value).
  3. Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().
  4. If we override one method, then we should override the other method as well.

6. Special Attention When Declared in an JPA Entity

If you’re dealing with an ORM, make sure always to use getters and never use the field references in hashCode() and equals(). Because in ORM, occasionally fields are lazy loaded and not available until we call their getter methods.

For example, In our Employee class if we use e1.id == e2.id. It is very much possible that id field is lazy-loaded. So, in this case, id field inside the methods might be zero or null, and thus resulting in incorrect behavior.

But if uses e1.getId() == e2.getId(), we can be sure even if the field is lazy-loaded, calling the field getter will populate the field first.

Happy Learning !!

Comments

Subscribe
Notify of
guest
74 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode