Skip to main content

Implementing Java Equals and HashCode Methods: A Guide with Examples and IntelliJ IDEA Usage

In Java, the equals() and hashCode() methods are used to compare objects and to generate unique hash codes for objects, respectively. By default, these methods are inherited from the Object class, which means that they compare objects based on their memory addresses. However, in many cases, we need to compare objects based on their contents rather than their memory addresses. To do so, we can provide our own implementations of equals() and hashCode() methods in our custom classes.

Example Class

Let's start with an example class that we will use throughout this blog. Suppose we have a class called Person that represents a person's name and age. Here's the class definition:


public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
  

Implementing equals() Method

To implement the equals() method, we need to override the default implementation inherited from the Object class. The equals() method takes one argument, which is an object to compare with the current object. The method returns a boolean value, which indicates whether the two objects are equal.

Here's an example implementation of equals() method for the Person class:


@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return age == person.age &&
            Objects.equals(name, person.name);
}
  

Let's go through this implementation step by step:

  1. The first line checks whether the two objects are the same object (i.e., they have the same memory address). If so, they are equal, so we return true.
  2. The second line checks whether the argument object is null or whether it belongs to a different class than the current object. If either condition is true,we return false, because two objects of different classes cannot be equal.
  3. The third line casts the argument object to the Person type, because we know that the argument object is of the same class as the current object (thanks to the previous check).
  4. The fourth line checks whether the age fields of the two objects are equal.
  5. The fifth line checks whether the name fields of the two objects are equal using the equals() method of the Objects class, which handles null values properly.

It's important to note that the equals() method should obey the following contract:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals() comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

By following this contract, we ensure that the equals() method works correctly with other Java classes and libraries.

Implementing hashCode() Method

To implement the hashCode() method, we also need to override the default implementation inherited from the Object class. The hashCode() method returns an integer hash code that represents the object. Two objects that are equal according to the equals() method must have the same hash code. However, two objects with the same hash code do not have to be equal according to the equals() method.

Here's an example implementation of the hashCode() method for the Person class:


@Override
public int hashCode() {
    return Objects.hash(name, age);
}
  

The implementation uses the Objects.hash() method, which generates a hash code based on the values of the specified fields. In this case, we include both the name and age fields in the hash code .

It's important to note that the hashCode() method should also obey a contract:

  • If two objects are equal according to the equals() method, then calling hashCode() on each of the objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals() method, then calling hashCode() on each of the 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.

Generating equals() and hashCode() Methods with IntelliJ IDEA

IntelliJ IDEA provides a useful feature to generate equals() and hashCode() methods automatically. This can save us time and help to avoid mistakes.

To generate these methods, follow these steps:

  1. Right-click on the class in the project view and select GenerateEquals and HashCode from the context menu.
  2. In the dialog that appears, select the fields that should be used in the equals() and hashCode() methods.
  3. Click OK.

Here's an example of how to generate equals() and hashCode() methods for the Person class using IntelliJ IDEA:

  1. Open the Person class in IntelliJ IDEA.
  2. Right-click on the class in the project view and select GenerateEquals and HashCode from the context menu.
  3. In the dialog that appears, select the name and age fields.
  4. Click OK.

IntelliJ IDEA will generate the following code for the equals() and hashCode() methods:


@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return age == person.age &&
            Objects.equals(name, person.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}
  

The generated code is similar to the example we implemented manually earlier. The equals() method checks whether the objects are of the same class and have the same values for the name and age fields. The hashCode() method uses the Objects.hash() method to generate a hash code based on the name and age fields.

Implementing the equals() and hashCode() methods is important when we want to compare objects for equality or store them in hash-based data structures. These methods should be implemented in a way that is consistent with each other and follow the contracts defined by Java.

Manually implementing these methods can be tedious and error-prone. IntelliJ IDEA provides a useful feature to automatically generate these methods based on the fields of the class. This can save us time and help avoid mistakes.

Overall, understanding how to implement the equals() and hashCode() methods is an important part of Java programming, and using tools like IntelliJ IDEA can make the process easier and more efficient.

Code Examples

Here's the complete code for the Person class with the equals() and hashCode() methods implemented manually:


public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
  

Here's the complete code for the Person class with the equals() and hashCode() methods generated by IntelliJ IDEA:


public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
  

As you can see, the code for both classes is the same. The only difference is the way the equals() and hashCode() methods are implemented.

Comments