Override equals() and hashCode() Methods in Java

Introduction

Here I will discuss how to override equals() and hashCode() methods in Java. In Java language the important contract is whenever you override one of the methods (equals() and hashCode()), then you must override the other method. So it means that when you override method equals() then you must override hashCode() or vice versa.

Features of equals() and hashCode()

  • In Java, every object has access to the equals() method because it is inherited from the Object class. However, this default implementation just simply compares the memory addresses of the objects.
  • You can override the default implementation of the equals() method defined in java.lang.Object class.
  • If you override the equals(), you must override hashCode() otherwise a violation of the general contract for Object.hashCode() will occur.
  • Since HashMap and Hashtable in Java relies on equals() and hashCode() method for comparing keys and values.

Rules of Overriding equals() and hashCode()

Java provides the following rules to override equals() method Java:

  1. Reflexive: Object must be equal to itself.
  2. Symmetric: If a.equals(b) is true then b.equals(a) must be true.
  3. Transitive: If a.equals(b) is true and b.equals(c) is true then c.equals(a) must be true.
  4. Consistent: Multiple invocations of equals() method must result same value until any of properties are modified.
  5. Null Comparison: Comparing any object to null must be false and should not result in NullPointerException.

Contract between equals() and hashCode()

Here is the contract, copied from the java.lang.Object specialization.

public native int hashCode() method returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.Hashtable.

The general contract of hashCode() is:

  1. Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode() method must consistently return the same integer, provided no information used in equality comparison on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  2. If two objects are equal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce the same integer result.
  3. It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode() method on each of the two 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.

As much as is reasonably practical, the hashCode() method defined by class Object does return distinct integers for distinct objects. This is typically implemented by converting the internal address of the object into an integer.

The default implementation of equals() method checks to see if the two objects have the same identity.

Similarly, the default implementation of the hashCode() method returns an integer based on the object’s identity and is not based on the values of instance (and class) variables of the object. No matter how many times the values of its instance variables (data fields) change, the hash code calculated by the default hashCode() implementation does not change during the life of the object.

Example of equals() and hashCode()

Consider the following code, I have overridden equals() method to check if two objects are equal based on the values of their instance variables.

Two objects may be stored at different memory addresses but may still be equal base on their instance variable.

public class Customer {
  private long id;
  private int name;

  public Customer(long id, int name) {
    super();
    this.id = id;
    this.name = name;
  }

  public boolean equals(Object obj) {
    //null instanceof Object will always return false
    if (!(obj instanceof Customer))
      return false;
    if (obj == this)
      return true;
    return  this.id == ((Customer) obj).id &&
            this.name == ((Customer) obj).name;
  }

  public static void main(String[] args) {
    Map m = new HashMap();

    m.put(new Customer(2345891234L,0),"Jeff Smith");

    System.out.println(m.get(new Customer(2345891234L,0)));
  }
}

Compile and run the above code, the output result is:

null

What went wrong?

The two instances of Customer are logically equal according to the class’s equals() method. Because the hashCode() method is not overridden, these two instances’ identities are not in common to the default hash code implementation. Therefore, the hashCode() returns two seemingly random numbers instead of two equal numbers. Such behavior violates the contract between equals() and hashCode() methods.

Equal objects must have equal hash codes – the rule defined in the hash code contract.

Fixing the Problem

Let’s provide a simple hashCode() method to fix this problem:

public class Customer {
  private long id;
  private int name;

  public Customer(long id, int name) {
    super();
    this.id = id;
    this.name = name;
  }

  public boolean equals(Object obj) {
    //null instanceof Object will always return false
    if (!(obj instanceof Customer))
      return false;
    if (obj == this)
      return true;
    return  this.id == ((Customer) obj).id &&
            this.name == ((Customer) obj).name;
  }

  public int hashCode() {
    int result = 0;
    result = (int)(id/12) + name;
    return result;
  }

  public static void main(String[] args) {
    Map m = new HashMap();

    m.put(new Customer(2345891234L,0),"Jhon Smith");

    System.out.println(m.get(new Customer(2345891234L,0)));
  }
}

Compile and run the above code, the output result is:

Jhon Smith

If two or more objects are equal then they must have same hashCode() but if two or more objects have same hashCode() then they are not necessarily be equal.

If two or more objects are equal then they must have same hashCode()

hashCode() is used for only hash based collections such as HashMap and HashSet but not for ArrayList, LinkedList, because these are not hash based collections.

So let’s say an object is searched either in a HashMap or in a HashSet on the basis of hashCode() and if the same hashCode() found in the HashSet or HashMap then only search moves to the equals() method to compare the objects whether they are equal or not.

Note if two equal objects are allowed to have different hashCode(), then while searching for a particular hashCode() it will never return a correct result and hence comparison will never happen using equals(), and result will declare those two objects to be unequal though those two objects are equal.

If two or more objects have same hashCode() then they are not necessarily be equal

You may know that the generated hashCode() for each object is of type int value, so you can generate maximum of 232 unique hashCode()s in total. So if you want to store more objects in HashSet or HashMap than 232 then there must be collision amongst the objects’ hashCode(). So you don’t have any other option rather than to assign the same hashCode() to two or more different objects and thus the above statement is true.

Hence it can be concluded from the above explanation:

Two or more same objects must have same hashCode().

Two or more different objects can also have same hashCode().

Hope you got an idea on hashCode() and equals() methods and how they work together.

Leave a Reply

Your email address will not be published. Required fields are marked *