Monday, 10 February 2014

Why Override hashCode() when overriding equals()

Prerequisite
Assume you are working in Web-Application
page1.jsp
<%
request.setAttribute("date",new java.util.Date());
%>
page2.jsp
<%
String str=new String("date");
Date dt=(Date)request.getAttribute(str);
%>
Note:-
In page1.jsp, In request.setAttribute(-,-) ,1st parameter is String object
In page2.jsp,In request.getAttribute(-),the parameter is String object Both these String object are different   object totally.
In page2.jsp we cannot get the same object as there in page1.jpg
Lets Come back to topic now
Lets Assume we have 2  different employee Object.
2 different Object means 2 different memory allocation.
However we would like to declare it equal,only when all the property of both Employee class objects are   equals
Employee.java
public class Employee {
 private int empId;
 private String empName;

 public Employee(int empId, String empName) {
  this.empId = empId;
  this.empName = empName;
 }
//You can also use getter()/setter()
}
The default implementation of equals() is,it checks if the memory reference is equal or not.
However,Our intention is,We want to declare 2 objects of Employee class equal,if they have same empId and empName.
Override the equals(Object o) method
public boolean equals(Object obj)
{
     // null instanceof Object will always return false
     if (!(obj instanceof Employee)) return false;
     else if (obj == this) return true;
     else {
         Employee emp = (Employee) obj;
         return this.empId == emp.empId && this.empName == emp.empName;
    }
}
If obj is not the Object of Employee class,its defintely not equal,so it returns false
If obj is the current Object of Employee class,then its definately equals,so it returns true
else,we check for equality by checking name,id on each Employee Object,so it returns true/false accordingly
Even though we are declaring 2 Objects are equal based on the above condition,please note,that there hashCode are different,because they are 2 different objects
The Complete Employee.java class would be
Employee.java
public class Employee {
    int empId;
    String empName;

    public Employee(int empId, String empName) {
        this.empId = empId;
        this.empName = empName;
    }//constructor

    public boolean equals(Object obj) {
        // null instanceof Object will always return false
        if (!(obj instanceof Employee))
        return false;
        else if (obj == this)
        return true;
        else {
            Employee emp = (Employee) obj;
            return this.empId == emp.empId && this.empName == emp.empName;
            //returns true if  empId and empName of both objects are equal.else returns false
        }//else
    }//equal()

}//class

Lets Create Another class,that uses HashMap

HASHMAPDEMO.JAVA


import java.util.HashMap;
public class HashMapDemo {
 public static void main(String[] args) {
  Employee emp1 = new Employee(101, "Satya");
  Employee emp2 = new Employee(101, "Satya");

  HashMap map = new HashMap();
  map.put(emp1, "Mr Satya");

  System.out.println(map.get(emp2));
 }
}

Ouput:- null
What went Wrong?
According to the equals(-) of Employee class,emp1 and emp2 are equal,Their property values are same. According to the HashCode Contract, If two objects are equals,there hashCode() should be equal But in our case,We have not overriden hashCode(),so Object class hashCode() gets executed,which checks the memory locations are equal or not.
Since 2 Object has different memory location,hashCode() returns different value.
We broke its rules,and hence output is null.
Solution:-
Also override the hashCode() in Employee class,and make sure it returns same value for equal Objects.  You may write any logic to simply return an integer.
Add the hashCode() to Employee class

public int hashCode()
 {
  return empId*1000;//returning any integer
 }
Now Run your application You will get the correct O/p.
What was the above Prerequisite for?
Let See how this concept is used in real time.
Assume You have 2 jsp,where in 1 jsp you have stored certain value in hashMap,and stored that hashMap in request scope.
page1.jsp
<%
Employee emp1=new Employee(101,"satya");
Employee emp2=new Employee(102,"kumar");

HashMap myMap=new HashMap();
myMap.put(emp1,"Mr.satya");
myMap.put(emp2,"Mr.kumar");

request.setAttribute("obj",myMap);
%>
Now from other jsp,you get the hashMap object.That hashMap contain some key value pairs and you want to retrieve certain value based on the key.

page2.jsp
<%
HashMap myMap2=(HashMap)request.getAttribute("obj");
//In order to retrieve "satya",I have to pass emp object with 101,satya as member variable value
//Remember this is a brand new object we are creating,and we cannot access emp1 of page1 directly,unless //you store it in request attribute.Brand new object means different hashCode

Employee emp=new Employee(101,"satya");

String val=(String)myMap2.get(emp); //returns you  a big big null,if you have not overriden hashCode(),

%>
This is where you need to override hashCode().
Hope this article proves to be a valuable one.
Any bugs/queries.We would like to hear from You.
Thank you for reading this article.

No comments:

Post a Comment