CodeGym /Java Blog /Java Objects /Equals Method in Java: Best Practices
Author
Milan Vucic
Programming Tutor at Codementor.io

Equals Method in Java: Best Practices

Published in the Java Objects group
Hi! Today we'll talk about two important methods in Java: equals() and hashCode(). This isn't the first time we've met them: the CodeGym course begins with a short lesson about equals() — read it if you've forgotten it or haven't seen it before... equals and hashCode methods: best practices - 1In today's lesson, we'll talk about these concepts in detail. And believe me, we have something to talk about! But before we move on to the new, let's refresh what we've already covered :) As you remember, it is usually a bad idea to compare two objects using the == operator, because == compares references. Here is our example with cars from a recent lesson:

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
Console output:

false
It seems we've created two identical Car objects: the values of the corresponding fields of the two car objects are the same, but the result of the comparison is still false. We already know the reason: the car1 and car2 references point to different memory addresses, so they are not equal. But we still want to compare the two objects, not two references. The best solution for comparing objects is the equals() method.

equals() method

You may recall that we don't create this method from scratch, rather we override it: the equals() method is defined in the Object class. That said, in its usual form, it is of little use:

public boolean equals(Object obj) {
   return (this == obj);
}
This is how the equals() method is defined in the Object class. This is a comparison of references once again. Why did they make it like that? Well, how do the language's creators know which objects in your program are considered equal and which are not? :) This is the main point of the equals() method — the creator of a class is the one who determines which characteristics are used when checking the equality of objects of the class. Then you override the equals() method in your class. If you don't quite understand the meaning of "determines which characteristics", let's consider an example. Here's a simple class representing a man: Man.

public class Man {

   private String noseSize;
   private String eyesColor;
   private String haircut;
   private boolean scars;
   private int dnaCode;

public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
   this.noseSize = noseSize;
   this.eyesColor = eyesColor;
   this.haircut = haircut;
   this.scars = scars;
   this.dnaCode = dnaCode;
}

   // Getters, setters, etc.
}
Suppose we're writing a program that needs to determine whether two people are identical twins or simply lookalikes. We have five characteristics: nose size, eye color, hair style, the presence of scars, and DNA test results (for simplicity, we represent this as an integer code). Which of these characteristics do you think would allow our program to identify identical twins? equals and hashCode methods: best practices - 2Of course, only a DNA test can provide a guarantee. Two people can have the same eye color, haircut, nose, and even scars — there are a lot of people in the world, and it's impossible to guarantee that there aren't any doppelgängers out there. But we need a reliable mechanism: only the result of a DNA test will let us make an accurate conclusion. What does this mean for our equals() method? We need to override it in the Man class, taking into account the our program's requirements. The method should compare the int dnaCode field of the two objects. If they are equal, then the objects are equal.

@Override
public boolean equals(Object o) {
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Is it really that simple? Not really. We overlooked something. For our objects, we identified only one field that is relevant to establishing object equality: dnaCode. Now imagine that we have not 1, but 50 relevant fields. And if all 50 fields of two objects are equal, then the objects are equal. Such a scenario is also possible. The main problem is that establishing equality by comparing 50 fields is a time-consuming and resource-intensive process. Now imagine that in addition to our Man class, we have a Woman class with exactly the same fields that exist in Man. If another programmer uses our classes, he or she could easily write code like this:

public static void main(String[] args) {
  
   Man man = new Man(........); // A bunch of parameters in the constructor

   Woman woman = new Woman(.........); // The same bunch of parameters.

   System.out.println(man.equals(woman));
}
In this case, checking the field values is pointless: we can readily see that we have objects of two different classes, so there is no way they can be equal! This means we should add a check to the equals() method, comparing the classes of the compared objects. It's good that we thought of that!

@Override
public boolean equals(Object o) {
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
But maybe we've forgotten something else? Hmm... At a minimum, we should check that we are not comparing an object with itself! If references A and B point to the same memory address, then they are the same object, and we don't need to waste time and compare 50 fields.

@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
It also doesn't hurt to add a check for null: no object can be equal to null. So, if the method parameter is null, then there is no point in additional checks. With all of this in mind, then our equals() method for the Man class looks like this:

@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
We perform all the initial checks mentioned above. At the end of day, if:
  • we are comparing two objects of the same class
  • and the compared objects are not the same object
  • and the passed object is not null
...then we proceed to a comparison of the relevant characteristics. For us, this means the dnaCode fields of the two objects. When overriding the equals() method, be sure to observe these requirements:
  1. Reflexivity.

    When the equals() method is used to to compare any object with itself, it must return true.
    We've already complied with this requirement. Our method includes:

    
    if (this == o) return true;
    

  2. Symmetry.

    If a.equals(b) == true, then b.equals(a) must return true.
    Our method satisfies this requirement as well.

  3. Transitivity.

    If two objects are equal to some third object, then they must be equal to each other.
    If a.equals(b) == true and a.equals(c) == true, then b.equals(c) must also return true.

  4. Persistence.

    The result of equals() must change only when the fields involved are changed. If the data of the two objects does not change, then the result of equals() must always be the same.

  5. Inequality with null.

    For any object, a.equals(null) must return false
    This is not just a set of some "useful recommendations", but rather a strict contract, set out in the Oracle documentation

Part 2: HashCode Method - Best Practices
Comments (9)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Vadim “迪姆哥” Level 35, Kazakhstan
31 July 2023
Author suggested a book "Effective Java but 2nd edition", but 3rd edition already out
matemate123 Level 50, Kraków, Poland
1 March 2023
Nice example with Ferrari's, I'll remember that.
Raed Saleh Level 25, Amman, Jordan
30 March 2021
very good article as usual in the last example in this article, I think you mentioned a point about it in previous lesson as follows if 2 objects have different hashcode java machine will not go to equal method at all and it will directly return false this is built in feature to save time if (this.hashcode() == o.hashcode()) { this.equal(o); } return false;
Shilpa nori Level 34, Rochester, United States
25 February 2021
The exercises and their solutions are making sense now. Hope this article was presented before the task.
Jackson Cummins Level 24, United States
14 February 2021
Great article. This topic is finally starting to make sense
Fadi Alsaidi Level 34, Carrollton, TX, USA
2 August 2020
Should have been the lesson about equality and hashcode to begin with!!!!!! Why beat around the bush?
BlueJavaBanana Level 37
29 June 2020
Really interesting. Great article guys!
Darek Level 41, Katowice, Poland
24 June 2020
Very good article, thanks