The equals() method in Java

How to properly override the equals() method in Java?

 14
Author: Alex Chermenin, 2010-12-07

5 answers

Basic principles of when designing the equals method:

  1. Reflexivity: for any non-null x objects, x.equals(x) must return true.
  2. Symmetry: for any non-null objects x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
  3. Transitivity: for any non-null objects x, y, and z, if x. equals (y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  4. Persistence: a repeated call to the equals() method must return the same value until some value of the object's properties is changed. That is, if two objects are equal, they will be equal as long as their properties remain unchanged.
  5. For any non-null x objects, x.equals(null) should return false.

You should also pay attention to the fact that the argument of the method equals always is an object of the Object class. For example, when redefining a method for the Person class, we must use the following signature: public boolean equals(Object person). Often beginners make an error and use the public boolean equals(Person person) signature, which leads to an overload of the equals method.

The equals method is related to the hashCode method: for identical objects, the value returned by the hashCode method must be the same. Therefore, both methods are often overridden.

 14
Author: Pavel Parshin, 2016-01-20 10:25:21

I'll give you an example that will make everything clear, and don't forget that when you redefine the equals() method, you need to redefine the hashCode () method. If the equals() method returns true for two objects, then their hash code must be the same. The converse is not true.

TestClass {
    private int id;
    public boolean equals(Object other)
    {
        if(!super.equals(other)) return false;
        if (this == other) return true;
        if (other == null) return false;
        if(this.getClass() != other.getClass()) return false;
        TestClass otherObj = (TestClass)other;
        return this.id == otherObj.id;
    }
    public int hashCode()
    {   
        return 76+133*id;
    }
}
 11
Author: Nicolas Chabanovsky, 2010-12-20 13:50:55

Please explain the code. In particular, this line

if (!super.equals(other)) return false;

The main question is as follows. What do you think this line of quoted code does? Yes, the equals method of the parent class is called there. And who is the parent of our class in this case? According to the specification, this is java. lang. Object. So first, in the first line of your code, we call the equals method of the Object class. What do you think it does? It seems like this is known information, but for more information here is a quote from the source code

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

That is, it returns true only if we have references to the object that match. If we have 2 separate objects that are stored in memory in different places (even if their contents are the same), the references to them do not match! What does that mean? This means that, thanks to the string if(!super.equals(other)) return false;, your method equals will return true only if we compare the object with itself! That's probably not what everyone would want, is it? I think, this line should be removed and then it will be more like the truth. But in general, every time you write your equals, you need to take into account the fact that once the creators of Java and the java.lang.Object class in particular wrote the conditions that the redefined equals method must meet, you can find them in the source code, or here.

And another note about hashCode. You mentioned only one of the contract requirements for hashCode: if 2 objects are considered equal (equals returns true), then the hashCode for them should return the same value. Next

The converse is not true.

In fact, in the contract, you can find - here is the continuation: "However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables". That is, it is necessary to take this fact into account, as well as another point from the same contract.

 5
Author: Serg, 2010-12-21 10:24:27

A variant of using equals in inheritance. Let there be a class point

public class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        // проверка на случай, если сравнение с самим собой
        if (obj == this)
            return true;
        if (obj.getClass() == this.getClass()) {
            Point point = (Point) obj;
            if (point.x == this.x && point.y == this.y)
                return true;
        }
        return false;
    }
}

Now I came up with the idea to write the Point3D extension and also redefine the equals{[3] method for it]}

public class Point3D extends Point {
    private int z;

    public Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    public boolean equals(Object obj) {
        if (super.equals(obj)) {
            /*
             * строки не нужны: эти проверки выполняется в базовом классе
             * if (this == other) return true; 
             * if (other == null) return false;
             * if(this.getClass() != other.getClass()) return false; 
             */
            Point3D p3d = (Point3D) obj;
            if (p3d.z == this.z)
                return true;
        }
        return false;
    }
}
 4
Author: Serg, 2010-12-20 13:46:49

We have a simple class of such a structure:

class A {
    final B someNonNullField;
    C someOtherField;
    int someNonStateField;
  }

This method of defining the equals method will be suitable in many situations:

public boolean equals(Object other) {
    // Не строго необходимо, но обычно неплохо для оптимизации
    if (this == other)
      return true;
    if (!(other instanceof A))
      return false;
    A otherA = (A) other;
    return 
      (someNonNullField.equals(otherA.someNonNullField))
        && ((someOtherField == null) 
            ? otherA.someOtherField == null 
            : someOtherField.equals(otherA.someOtherField)));
  }

Indeed, we should not forget about the hashCode method. this implementation will do:

public int hashCode() { 
    int hash = 1;
    hash = hash * 31 + someNonNullField.hashCode();
    hash = hash * 31 
                + (someOtherField == null ? 0 : someOtherField.hashCode());
    return hash;
  }

More details are written with this article from ibm deweloperworks.

 4
Author: Evgeniy, 2011-01-21 18:51:22