Wednesday 21 March 2012

Overriding hashCode() method in Java or Why always override hashcode() if overriding equals()?

Object class has following five non final methods.

1. clone()
2. equals(Object obj)
3. finalize()
4. hashCode()
5. toString()

Every Java class has Object as a superclass so by default all above methods are provided in every java class. In this article we will discuss in detail the concepts of hashCode() method. We will discuss why it is necessary to override hashCode method,Why always override hashCode() if overriding equals() and how we can override them.

Default implementation of hashCode() in Object class

The signature of hashCode is:

public int hashCode()

The default version of hashCode method present in the Object class returns a hash code value for the object and does return distinct integers for distinct objects.This is typically implemented by converting the internal address of the object into an integer.See the example given below

public class Main {

    public static void main(String args[]) {

        Student s1 = new Student("Ramesh");
        Student s2 = new Student("Ramesh");

        System.out.println("s1.hashCode()=" + s1.hashCode());
        System.out.println("s2.hashCode()=" + s2.hashCode());
    }

}

class Student {
    String name;

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

The output is

s1.hashCode()=9634993
s2.hashCode()=1641745

Notice that even if the objects s1 and s2 contain same data 'Ramesh' ,the hashCode returns different values for both s1 and s2.That means the Object class provided hashCode method implementation completely depends on the memory address of the object not its state.

Why to override hashCode() method

The int value returned from hashCode can be used with hash based Collection classes e.g. Hashtable, HashSet and HashMap etc.The hash based Collection classes store data as key-value pairs.The values are stored in different hash buckets based on the hashCode values returned for the corresponding keys.
A value can be retrieved from a hash table using the hashCode value of the corresponding key.The hashCode method should be overridden in such a way that the return value depends on the data stored in the key object not simply on the memory address.

Why always override hashCode() if overriding equals()?

If you override the equals(), you MUST also override hashCode() to make sure that if two objects are equal, then calling hashCode() on both objects must return the same value. Otherwise a violation of the general contract for Object.hashCode will occur which can lead to errors. Here is the contract, copied from the java.lang.Object specialization. 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 equals comparisons 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 hashtables.
      Overriding hashCode() method in Java

      The various methods to override hashCode() method are as follows.

      Override equals() and hashCode() In Eclipse and Netbeans


      In Netbeans


      1) Write your Class.
      2) Right click + insert code + Generate equals() and hashCode().

      In Eclipse

      1) Write your Class.2) Go to Source Menu + Generate hashCode() and equals()

      See one example
      public class Main {
      
          public static void main(String args[]) {
      
              Student s1 = new Student("Ramesh");
              Student s2 = new Student("Ramesh");
      
              System.out.println("s1.hashCode()=" + s1.hashCode());
              System.out.println("s2.hashCode()=" + s2.hashCode());
          }
      
      }
      
      class Student {
          String name;
      
          Student(String name) {
              this.name = name;
          }
      
          @Override
          public int hashCode() {
              final int prime = 31;
              int result = 1;
              result = prime * result + ((name == null) ? 0 : name.hashCode());
              return result;
          }
      
          @Override
          public boolean equals(Object obj) {
              if (this == obj)
                  return true;
              if (obj == null)
                  return false;
              if (getClass() != obj.getClass())
                  return false;
              Student other = (Student) obj;
              if (name == null) {
                  if (other.name != null)
                      return false;
              } else if (!name.equals(other.name))
                  return false;
              return true;
          }
         
      }
      


      The output is

      s1.hashCode()=-1854447397
      s2.hashCode()=-1854447397

      Notice that here hash code values of s1 and s2 are same ,the reason is that s1 and s2 contain the same data 'Suresh'.The overridden hashCode method return value depends on the state of the object not the memory address.

      Generate hashCode using Apache HashCodeBuilder


      Rather than using Eclipse generated equals() and hashCode() methods we could use the Apache Commons EqualsBuilder and HashCodeBuilder.See one example

      import org.apache.commons.lang3.builder.EqualsBuilder;
      import org.apache.commons.lang3.builder.*;
      
      public class Main {
      
          public static void main(String args[]) {
              Student s1 = new Student("Suresh");
              Student s2 = new Student("Suresh");
              System.out.println("s1.equals(s2)=" + s1.equals(s2));
              System.out.println("s1.hashCode()=" + s1.hashCode());
              System.out.println("s2.hashCode()=" + s2.hashCode());
          }
      }
      
      class Student {
      
          String name;
      
          public Student(String name) {
      
              this.name = name;
          }
      
          @Override
          public int hashCode() {
              HashCodeBuilder builder = new HashCodeBuilder();
              builder.append(name);
              return builder.toHashCode();
          }
      
          @Override
          public boolean equals(Object obj) {
              if (getClass() == obj.getClass()) {
                  EqualsBuilder builder = new EqualsBuilder();
                  Student s = (Student) obj;
                  builder.append(name, s.name);
                  return builder.isEquals();
              }
              return false;
          }
      }
      

      The output is
      s1.equals(s2)=true
      s1.hashCode()=-1807198273
      s2.hashCode()=-1807198273

      1 comment:

      1. good article keep it up give more real time scenerio

        ReplyDelete