Comparing Java objects with equals() and hashcode()

What is the contract between equals() and hashcode()? Learn how these methods work together when comparing objects in Java.

Comparing objects in Java with equals() and hashcode()
Cagkan Sayin/Shutterstock

Java's equals() and hashcode() are two methods that work together to verify if two objects have the same value. You can use them to make object comparisons easy and efficient in your Java programs.

Java equals() and hashcode()

In this article, you'll learn:

  • Why override equals() and hashcode() in Java?
  • How to compare Java objects with equals()
  • How to identify Java objects with hashcode()
  • How to use equals() and hashcode() with collections

You'll also get:

  • Guidelines for using equals() and hashcode()
  • Rules for making object comparisons with equals() and hashcode()
  • Mistakes to avoid when using equals() and hashcode()
  • What to remember about equals() and hashcode()

Why override equals() and hashcode() in Java?

Method overriding is a technique where the behavior of the parent class or interface is written again (overridden) in the subclass in order to take advantage of Polymorphism. Every Object in Java includes an equals() and a hashcode() method, but they must be overridden to work properly.

To understand how overriding works with equals() and  hashcode(), we can study their implementation in the core Java classes. Below is the equals() method in the Object class. The method is checking whether the current instance is the same as the previously passed Object.


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

When the hashcode() method is not overridden, the default method in the Object class will be invoked. This is a native method, which means it will be executed in another language like C, and will return some code regarding the object's memory address. (It’s not that important to know exactly how this method works unless you are writing JDK code.)


@HotSpotIntrinsicCandidate
public native int hashCode();

When the equals() and hashcode() methods are not overridden, you will see the above methods invoked instead. In this case, the methods do not fulfill the real purpose of equals() and hashcode(), which is to check whether two or more objects have the same values.

As a rule, when you override equals() you must also override hashcode().

How to compare Java objects with equals()

We use the equals() method to compare objects in Java. To determine if two objects are the same, equals() compares the values of the objects’ attributes:


public class EqualsAndHashCodeExample {

    public static void main(String... equalsExplanation) {
        System.out.println(new Simpson("Homer", 35, 120)
                 .equals(new Simpson("Homer",35,120)));
        
        System.out.println(new Simpson("Bart", 10, 120)
                 .equals(new Simpson("El Barto", 10, 45)));
        
        System.out.println(new Simpson("Lisa", 54, 60)
                 .equals(new Object()));
    }
	
    static class Simpson {

        private String name;
        private int age;
        private int weight;

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

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Simpson simpson = (Simpson) o;
            return age == simpson.age &&
                    weight == simpson.weight &&
                    name.equals(simpson.name);
        }
    }

}

In the first comparison, equals() compares the current object instance with the object that was passed. If the two objects have the same values, equals() returns true.

In the second comparison, equals() checks to see whether the passed object is null, or if it’s typed as a different class. If it’s a different class then the objects are not equal.

Finally, equals() compares the objects’ fields. If two objects have the same field values, then the objects are the same.

Object comparisons

Now, let’s view the results of these comparisons in our main() method. First, we compare two Simpson objects:


System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120)));

The objects here are identical, so the result will be true.

Next, we compare two Simpson objects again:


System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

The objects here are nearly identical but their names are different: Bart and El Barto. Therefore the result will be false.

Finally, let’s compare a Simpson object and an instance of the class Object:


System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

In this case the result will be false because the class types are different.

equals() is not the same as ==

At first glance, the == operator and equals() method may appear to do the same thing, but they work differently. The == operator compares whether two object references point to the same object. For example:


System.out.println(homer == homer2);

In the first comparison, we instantiated two different Simpson instances using the new operator. Because of this, the variables homer and homer2 will point to different Object references in the memory heap. So we’ll have false as the result.

System.out.println(homer.equals(homer2));

In the second comparison, we override the equals() method. In this case only the names are compared. Because the name of both Simpson objects is “Homer” the result is true.

How to identify Java objects with hashcode()

We use the hashcode() method to optimize performance when comparing objects. Executing hashcode() returns a unique ID for each object in your program, which makes the task of comparing the whole state of the object much easier.

If an object’s hashcode is not the same as another object’s hashcode, there is no reason to execute the equals() method: you just know the two objects are not the same. On the other hand, if the hashcode is the same, then you must execute the equals() method to determine whether the values and fields are the same.

Here’s a practical example with hashcode().


public class HashcodeConcept {

    public static void main(String... hashcodeExample) {
        Simpson homer = new Simpson(1, "Homer");
        Simpson bart = new Simpson(2, "Homer");

        boolean isHashcodeEquals = homer.hashCode() == bart.hashCode();

        if (isHashcodeEquals) {
            System.out.println("Should compare with equals method too.");
        } else {
            System.out.println("Should not compare with equals method because " +
                    "the id is different, that means the objects are not equals for sure.");
        }
    }

     static class Simpson {
        int id;
        String name;

        public Simpson(int id, String name) {
            this.id = id;
            this.name = name;
        }

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

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

A hashcode() that always returns the same value is valid but not very effective. In this case the comparison will always return true, so the equals() method will always be executed. There is no performance improvement in this case.

Using equals() and hashcode() with Java collections

The Set interface is responsible for ensuring no duplicate elements are inserted in a Set subclass. The following are some of the classes that implement the Set interface:

Only unique elements may be inserted into a Set, so if you want to add an element to the HashSet class (for example), you must first use the equals() and hashcode() methods to verify the element is unique. If the equals() and hashcode()methods weren’t overridden in this case, you would risk inserting duplicate elements in the code.

In the code below, we’re using the add method to add a new element  to a HashSet object. Before the new element is added, HashSet checks to see whether the element  already exists in the given collection:


if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
       break;
       p = e; 

If the object is the same, the new element won’t be inserted.

Guidelines for using equals() and hashcode()

You should only execute an equals() method for objects that have the same unique hashcode ID. You should not execute equals() when the hashcode ID is different.

This principle is mainly used in Set or Hash collections for performance reasons.

Object comparison with equals() and hashcode()

When a hashcode() comparison returns false, the equals() method must also return false. If the hashcode is different, then the objects definitely are not equal.

When the equals() method returns true, it means that the objects are equal in all values and attributes. In this case,  the hashcode comparison must be true as well.

Take the equals() and hashcode() challenge!

It’s time to test your skills with equals() and hashcode().  Your goal in this challenge is to figure out the output of the two equals() method comparisons and guess the size of the Set collection.

To start, study the following code carefully:


public class EqualsHashCodeChallenge {

    public static void main(String... doYourBest) {
        System.out.println(new Simpson("Bart").equals(new Simpson("Bart")));
        Simpson overriddenHomer = new Simpson("Homer") {
            public int hashCode() {
                return (43 + 777) + 1;
            }
        };

        System.out.println(new Simpson("Homer").equals(overriddenHomer));

        Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge")));
        set.add(new Simpson("Homer"));
        set.add(overriddenHomer);

        System.out.println(set.size());
    }

    static class Simpson {
        String name;

        Simpson(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object obj) {
            Simpson otherSimpson = (Simpson) obj;
            return this.name.equals(otherSimpson.name) &&
                    this.hashCode() == otherSimpson.hashCode();
        }

        @Override
        public int hashCode() {
            return (43 + 777);
        }
    }

}

Remember, analyze the code first, guess the result, then run the code. Your goal is to improve your skill with code analysis and absorb core Java concepts to make your code more powerful. Choose your answer before checking the correct one below.

A)


true
true
4

B)


true
false
3 

C)


true
false
2

D)


false
true
3 

Solving the equals() and hashcode() challenge

In the first equals() method comparison, the result is true because the state of the object is exactly the same and the hashcode() method returns the same value for both objects.

In the second equals() method comparison, the hashcode() method is being overridden for the overridenHomer variable. The name is “Homer” for both Simpson objects, but the hashcode() method returns a different value for overriddenHomer. In this case, the final result from the the equals() method will be false because the method contains a comparison with the hashcode.

You might notice that the size of the collection is set to hold three Simpson objects. Let’s check this in a detailed way.

The first object in the set will be will be inserted normally:


new Simpson("Homer");

The next object will be inserted normally, as well, because it holds a different value from the previous object:


new Simpson("Marge");

Finally,  the following Simpson object has the same value as the first object. In this case the object won’t be inserted:


set.add(new Simpson("Homer"));

As we know, the overridenHomer object uses a different hashcode value from the normal Simpson(“Homer”) instantiation. For this reason, this element will be inserted into the collection:


overriddenHomer;

Answer key

The answer is B. The output would be:


true 
false 
3 

Mistakes to avoid with equals() and hashcode()

1 2 Page 1
Page 1 of 2