Saturday 3 January 2015

Equals and hascode method in java

Example of equals and hashcode method in java


This document explains :Why equals and hashcode methods are require and how to implement them.

If you want to use a class as a key in MAP/SET, provide proper hashcode() and equals() method implementation for that class.

Reason: If you don't provide implementation for these  methods class will inherit these from superclass implementation of these methods(generally Object class) and map/set may behave incorrectly.
Lets’ take an example of employee class

Employee.java

public class Employee
{
       String employeeId;
       enum EmpType{FULLTIMEPARTTIMEINTERN};
       String type;
       String joiningYear;
      
       public Employee(String empId, String type, String jy)
       {
              this.employeeId = empId;
              this.type = type;
              this.joiningYear = jy;
       }
      
       public void show()
       {
              System.out.println("Employee ID : "+employeeId + "  Employee Type "type+" joining year  "+joiningYear);
       }
      
       @Override
       public String toString()
       {
              return employeeId + "  "+type + "   "+joiningYear;
       }
}


 MainClass1.java

import java.util.HashMap;
import java.util.Map;
public class MainClass1 {
                public static void main(String[] args)
                {
                                Map<Employee,Float> map = new HashMap<Employee, Float>();
                                Employee e1 = new Employee("Mayank Sharma", EmpType.FULLTIME.toString(), "2013");
                                Employee e2 = new Employee("Nikhil Kumar", EmpType.INTERN.toString(), "2014");
                                Employee e3 = new Employee("Love Sharma", EmpType.PARTTIME.toString(), "2013");               
                                Employee e4 = new Employee("Kapil Kumar", EmpType.FULLTIME.toString(), "2002");                                   Employee e5 = new Employee("Neeraj Panwar", EmpType.INTERN.toString(), "2014");
                                Employee e6 = new Employee("David Buther", EmpType.PARTTIME.toString(), "2011");
                                Employee e7 = new Employee("David Buther", EmpType.PARTTIME.toString(), "2011");
                                map.put(e1, (float) 500000);
                                map.put(e2, (float) 50000);
                                map.put(e3, (float) 400000);
                                map.put(e4, (float) 1500000);
                                map.put(e5, (float) 50000);
                                map.put(e6, (float) 80000);
                                map.put(e7, (float) 90000);
                                for(Employee employee: map.keySet())
                                {
                                                employee.show();
                                                System.out.println(map.get(employee));
                                }
                }
}

Output:

Employee ID : David Buther  Employee Type PARTTIME joining year  2011
90000.0
Employee ID : Neeraj Panwar  Employee Type INTERN joining year  2014
50000.0
Employee ID : Kapil Kumar  Employee Type FULLTIME joining year  2002
1500000.0
Employee ID : Nikhil Kumar  Employee Type INTERN joining year  2014
50000.0
Employee ID : Love Sharma  Employee Type PARTTIME joining year  2013
400000.0
Employee ID : David Buther  Employee Type PARTTIME joining year  2011
80000.0
Employee ID : Mayank Sharma  Employee Type FULLTIME joining year  2013
500000.0

Here object “e7” and “e6” have same hash code, but e7 should have override the e6 but map contains both e6 and e7 which shows that but e6 and e7 should be same since they have same value set.

Now override equals method in Employee class:
       @Override
       public boolean equals(Object o)
       {
              if(o == this)
                     return true;
              if(!(o instanceof Employee1))
              {
                     return false;
              }
              Employee1 employee1 = (Employee1)o;
              return this.employeeId.equals(employee1.employeeId)
                     && this.type.equals(employee1.type)
                     && this.joiningYear.equals(employee1.joiningYear);
       }

Add try to run the same program.
You will get same output. It is because hashcode() is not overridden in employee class and we are using the same previous hashcode which says that two different object will have same hashcode.
Now lets, override the hashcode() in Employee class :
       @Override
       public int hashCode()
       {
              return 42;
       }

All the objects have same hash code, so they all will added in a particular entry of linked list and hence form a linked list.
Now run the program.
Wow. You will get the desired output.
Output:

Employee ID : Mayank Sharma  Employee Type FULLTIME joining year  2013
500000.0
Employee ID : Nikhil Kumar  Employee Type INTERN joining year  2014
50000.0
Employee ID : Love Sharma  Employee Type PARTTIME joining year  2013
400000.0
Employee ID : Kapil Kumar  Employee Type FULLTIME joining year  2002
1500000.0
Employee ID : Neeraj Panwar  Employee Type INTERN joining year  2014
50000.0
Employee ID : David Buther  Employee Type PARTTIME joining year  2011
90000.0
There is no duplicate entry for employee with employee name : David Buther
You can provide following implementation of hashcode for better performance:
       @Override
       public int hashCode()
       {
              int result  =17;
              result = 31* result + employeeId.hashCode();
              result = 31*result +  type.hashCode() ;
              result = result* 31 +joiningYear.hashCode();
              return result;
       }

This method avoid collision and final map will not looks like a linked list as in the previous case.
Where the equals and hashcode methods are use?
Let’s take the HashMap put method implementation:
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

And here is the implementation of hash() :

    final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }





No comments:

Post a Comment