equals and hashCode contract!

The general contract of hashCode is:

•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.

•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.

•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.

I always has a doubt about contract 2. So, I try o clarify this doubt with an example. Consider a case when two objects are equals but returns difference hashCode. Here is one class with bas hashCode and equals methos. I am specifying this class only for example.

class MyClass{
private int value;
public MyClass(int value){
this.value = value;
}
public String toString(){
return "" + value;
}
public boolean equals(Object obj){
return true;
}
}

Here we always returns true from equals methods, which says that all the objects are equals. But returns different values of hashCode(its computed by Java Object hashCode method, which is the hex value of address of object, and it will be different for each object).
Now create a HashSet and store these value:-

import java.util.*;
public class test{
public static void main(String args[]){
HashSet set = new HashSet();
MyClass myObject1 = new MyClass(12);
MyClass myObject2 = new MyClass(13);
set.add(myObject1);
set.add(myObject2);
System.out.println("HashSet is:"+set);
HashSet set2 = new HashSet();
set2.add("vivek");
set2.add("vivek");
System.out.println("HashSet2 is:"+set2);
}
}

Here is the output:-

C:\DOCUME~1\Vivek>java test
HashSet is:[12, 13]
HashSet2 is:[vivek]

If you see the output you realize that even though myObject1 and myObject2 are equals(because we override equals method to always return true) they are stored as different in HashSet, which is not the case with “vivek”.

The reason behind this is that, the concept of equality is different in “hash based” collection(i.e HashMap, HashSet etc). Given an object the system store these values in two levels, first at bucket level and another the values in side the bucket.

When we insert myObject1, system will calculate the hash value by calling hashCode method and this value determine the exact bucket under which this “myObject1” will fall. Just assume that this is mapped to bucket1.

Bucket1 —> myObject1

When we add another value system will first verify if the value is already present. In normal case this will happens by just calling equals method, but in case of “hash based collections” this happens in two steps.
First hashCode of myObject1 is determined and this will decide the bucket, next system will search this bucket and calls equals method to check if any duplicate value is present. In this case myObject2 will be mapped to different bucket, because it has different hashCode than myObject1.(in ideal situation this should be mapped to bucket1) and hence system will fail to verify that already an equal object(myObject1) is inserted.

Bucket2 –> myObject2

And hence two duplicate values will be inserted in a set which is wrong.
The concept of equality is different in case of “hash based collection”, two objects are considered equal only of they are equal(pass equals()) and have same hashCode.

The same concept applies with HashTable also. Lets modify the program little bit:-

import java.util.*;
public class test{
public static void main(String args[]){
Hashtable table1 = new Hashtable();
MyClass myObject1 = new MyClass(12);
MyClass myObject2 = new MyClass(13);
table1.put(myObject1,"abcde");
table1.put(myObject2,"bcdeff");
System.out.println("Hashtable 1 is:"+table1);

Hashtable table2 = new Hashtable();
table2.put(“vivek”,”abcde”);
table2.put(“vivek”,”bcdeff”);
System.out.println(“Hashtable 2 is:”+table2);
}
}

Here is the output:-

C:\DOCUME~1\Vivek>java test
Hashtable 1 is:{13=bcdeff, 12=abcde}
Hashtable 2 is:{vivek=bcdeff}

Same reason as describe above applies here. myObject1 and myObject2 are treated differently even though they are equals, which is not the case with String (“vivek”)

Advertisements

One response to “equals and hashCode contract!

  1. Excellent article

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s