Wednesday, April 17, 2013

How to Override Equals, HashCode and CompareTo method in Java

Though modern IDE like Eclipse, IntelliJ or Netbeans allows you to generate equals, hashCode and compareTo methods for your value classes, it's equally important, you know how to do that by hand. By overriding equals and hashcode method by yourself, you know how they work, what kind of errors you can get, and most importantly, it's expected form you, as a Java programmer in any core Java interview. More often, you would see a coding question in Java, which ask you to override equals(), hashcode(), compare() and compareTo() methods for a value class. Since I have already shared some tips on How to override compareTo method in Java, and couple of example of writing your own Comparator in Java, here I am sharing another simple example of overriding equals and hashCode methods. If you know rules of overriding equals and hashCode, you might know that, whenever you override equals, you must have to override hashCode, otherwise your object will not behave properly on various collection classes e.g. Map or Set, which uses equals, compareTo, hashCode to implement there invariants e.g. Set implementations should not allow any duplicates.



Overriding Equals, HashCode and CompareTo in Java

Example of overriding equals hashcode and compareTo in JavaIn this Java program, we will override all three method for a Person class, which contains a String name, an integer id and a Date for date of birth. In order to override equals, you need to follow certain checks, e.g. checking null, checking type of object etc, Also your equals() method, when compared with null, should return false instead of throwing NullPointerException. See this post for detailed tips on overriding equals method in Java. For overriding hashCode, you need to choose a prime, usually 31, but you can also choose other prime numbers e.g. 37, 17 etc. The reason for choosing these prime numbers are to generate a uniquely distributed hashcode, which eventually helps to avoid collision, when used in hash based collections like Hashtable and HashMap. Another worth noting thing is using all variables, used in equals, in hashCode method to keep equals and hashCode consistent, and adhere to rules of overriding hashCode in Java. I have already discussed about Comparable and compareTo method, while sorting list of objects in Java. A simple implementation of compareTo must return negative number if current object is lesser than provided object, positive if current object is greater in order than provided and zero if both objects are equal. Another worth noting point is that, compareTo() and equals() must be consistent with each other, otherwise your object will not behave properly on Collection classes, which uses compareTo() for sorting e.g. TreeSet or TreeMap. Anyway, here is a simple example of overriding equals, hashcode and compareTo in Java.



Sample implementation of Equals, HashCode and CompareTo in Java

In this sample example of overriding equals, hashcode and compareTo method, we will use a class named Person which has 3 properties String name, int id and Date to represent date of birth.  We will also use Generics along with Comparable to provide a type safe implementation. Remember we have used getClass() method instead of instanceof operator, which means a Person class cannot be equal to its subclass, this could create problem if you are using this class in EJB or any application server, where there is a chance that same class is loaded by two separate class loader. On those cases it's better to use instanceof operator because it will allow a Class to be equal to its subclass if rest of properties matched. This is also true for framework like Hibernate, which provides proxy implementation, which is essentially sub class of entity classes. In short, use instanceof if your class can be loaded by multiple class loader or it can be used by framework to create proxies.
/**
 * Simple Java Class to represent Person with name, id and date of birth.
 *
 * @author Javin Paul
 */
public class Person implements Comparable<Person>{
   private String name;
   private int id;
   private Date dob;

    public Person(String name, int id, Date dob) {
        this.name = name;
        this.id = id;
        this.dob = dob;
    }
  
   @Override
   public boolean equals(Object other){
       if(this == other) return true;
      
       if(other == null || (this.getClass() != other.getClass())){
           return false;
       }
      
       Person guest = (Person) other;
       return (this.id == guest.id) &&
              (this.name != null && name.equals(guest.name)) &&
              (this.dob != null && dob.equals(guest.dob));
   }
  
    @Override
   public int hashCode(){
       int result = 0;
       result = 31*result + id;
       result = 31*result + (name !=null ? name.hashCode() : 0);
       result = 31*result + (dob  !=null ? dob.hashCode() : 0);
      
       return result;
   }

    @Override
    public int compareTo(Person o) {
        return this.id - o.id;
    }   
}



Sample JUnit test case for testing Equals and HashCode

Here is simplest of simple test case to verify equals and hashCode in Java. Remember this unit test is not enough, if you want to verify all properties of equals and hashCode. If you should write test cases to check if it verify key properties of equals and hashcode method e.g. if a equal b then b should also be equal to a,  or if a a equals b and b equals c then a and c should also be equal to each other. You must also verify whether compareTo() return 0 for equal object and return non zero value for non equal objects. 
import java.util.Date;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Simple example of using equals and hashCode method
 * @author Javin Paul
 */
public class EqualsAndHashCodeTest {   

  
   @Test
   public void testEquals(){
       Person james = new Person("James", 21, new Date(1980,12, 1));
       Person same = new Person("James", 21, new Date(1980,12, 1));
       Person similar = new Person("Harry", 21, new Date(1981,12,1));
      
       assertTrue(james.equals(same));
       assertTrue(james.hashCode() == same.hashCode());
    
       assertFalse(james.equals(null));
       assertFalse(james.equals(similar));
       assertTrue(james.hashCode() != similar.hashCode());
   } 
  
}


That's all on this simple example of overriding equals, hashcode and compareTo method in Java. As I said, these test are just to verify that equals and hashCode methods are working, you can add more test to verify compareTo, and other behaviors of all three methods.


Related Java Programming Articles and Tutorials from Java67

5 comments:

  1. Rules to Remember :

    1) Always override hashcode if you are overriding equals and vice-versa.

    2) Make sure your equals() method is consistent with compare() and compareTo() method, if you intend to use your object with either SortedSet or SortedMap.

    3) Always use getClass() to check type of object instead of using instanceof operator. Using instanceof, breaks symmetry property of equals method.

    4) A correct implementation of Equals method must satisfy following condition :

    a) Don't throw NullPointerException if compared with null.

    b) Must be symmetric i.e. if x.equals(y) is true and y.equals(x) must be true.

    c) Must be transitive i.e. if x.equals(y) is true and y.equals(z) is true than x.equals(z) must be true.

    Cheers

    ReplyDelete
  2. Can u give one example like how instanceof breaks symmetry property of equals method.

    ReplyDelete
    Replies
    1. As per symmety property of equals() method if x.equals(y) is true then y.equals(x) should also be true. If you have used instanceof operator in equals method to check type of object e.g. obj instanceof Type, then it will work for super class but not in sub class because sub instanceof Super is true but super instanceof Sub returns false. I thing following code sample, makes it more clear :

      import java.util.Date;



      public class EqualsSymmmetryTest {


      public static void main(String args[]){

      Ticket t = new Ticket(101, new Date() );
      MovieTicket m = new MovieTicket(101, new Date());

      System.out.println("t.equals(m) : " + t.equals(m));
      System.out.println("m.equals(t) : " + m.equals(t));

      }


      }

      class Ticket{

      protected int id;
      protected Date validity;

      public Ticket(int id, Date validity) {
      super();
      this.id = id;
      this.validity = validity;
      }

      @Override
      public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + id;
      result = prime * result
      + ((validity == null) ? 0 : validity.hashCode());
      return result;
      }

      @Override
      public boolean equals(Object obj) {
      if (this == obj)
      return true;
      if (obj == null)
      return false;

      if (!(obj instanceof Ticket))
      return false;

      Ticket other = (Ticket) obj;
      if (id != other.id)
      return false;
      if (validity == null) {
      if (other.validity != null)
      return false;
      } else if (!validity.equals(other.validity))
      return false;
      return true;
      }

      }

      class MovieTicket extends Ticket{

      public MovieTicket(int id, Date validity) {
      super(id, validity);
      }

      @Override
      public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + id;
      result = prime * result
      + ((validity == null) ? 0 : validity.hashCode());
      return result;
      }

      @Override
      public boolean equals(Object obj) {
      if (this == obj)
      return true;
      if (obj == null)
      return false;

      if (!(obj instanceof MovieTicket))
      return false;

      MovieTicket other = (MovieTicket) obj;
      if (id != other.id)
      return false;
      if (validity == null) {
      if (other.validity != null)
      return false;
      } else if (!validity.equals(other.validity))
      return false;
      return true;
      }

      }

      this will print

      t.equals(m) : true
      m.equals(t) : false

      However, there are cases where you must use instanceof operator instead of getClass() e.g. if you are writing an entity class for Hibernate framework, you must use instanceof because hibernate creates proxies by subclassing your entity class and if you use getClass() to check type, it will return false if compare with an instance of sub class, but instanceof will return true as shown in above example. Another place, where you should use instnaceof instead of getClass() is where you class can be loaded by two differnet class loader e.g. in case of EJB or web application. If same class is loaded by two different class loader then their getClass() would return differnet values.

      Delete
  3. public int hashCode(){
    int result = 0;
    result = 31*result + id;
    result = 31*result + (name !=null ? name.hashCode() : 0);
    result = 31*result + (dob !=null ? dob.hashCode() : 0);

    return result;
    }

    in the hash code generation logic prime number plays great role to avoid hash key duplicate, but in the above example some logical issue
    result = 31 * result + id ===> 0 + id ( because result is initialzed with zero ).
    prime number not playing any role in this example.

    ReplyDelete
    Replies
    1. But then, look at the next 2 lines:
      result = 31*result + (name !=null ? name.hashCode() : 0);
      => with result == id after first operation => result = 31*id + name.hashCode();
      next line:
      result = 31*result + (dob !=null ? dob.hashCode() : 0);
      => with result == value from above
      => result = 31*31*id + 31*(name.hashCode()) + dob.hashCode()
      [I left out the 0 if null part just for simplicity, but you see that the 31 DOES play a part.

      Only case is, if (id == 0 && name == null && dob == null)
      => final hashCode == 0.

      Delete

Java67 Headline Animator