This is 3rd part of java Serialization tutorial

Java Serialization Tutorial:

    Serialization in java Java Serialization interview questions and answers serialversionuid in java serialization externalizable in java Transient keyword in java Difference between Serializable and Externalizable in Java

Before understanding Externalizable interface, you need to have idea about Serialization.You can read more about Serialization at Serialization in java.
Java provides mechanism called serialization to persists java objects in a form of ordered or sequence of bytes that includes the object's data as well as information about the object's type and the types of data stored in the object.

Externalizable:

As name suggest it is externalilizing your serialization.If you want to customize your serialization mechanism then you can use it.It uses custom written mechanism to perform marshalling and unmarshalling of objects.Externalizable interface extends Serializable interface. If you implement this interface then you need to override following methods.
    @Override
    public void readExternal(ObjectInput arg0) throws IOException,

            ClassNotFoundException {

    }

    @Override
    public void writeExternal(ObjectOutput arg0) throws IOException {

    }

Now lets see how serialization happens:

At sender side:
JVM checks if class implements externalizable or not.If it does then serialize object using writeExternal() method.If it does not implement externalizable but implements serializable , object is serialized using ObjectOutputStream.

At receiver side:
When object is reconstructed and it is externalizable , an instance is created using no args constructor and readExternal is called.If it is not externalizable but serializable , object is reconstructed using ObjectInputStream.

Lets start with example same as we have used in Serialization in java.
Create Employee.java in src->org.arpit.java2blog

Employee.java:
package org.arpit.java2blog;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Employee implements Externalizable{ 

 int employeeId;
 String employeeName;
 String department;
 String nationality;

 public Employee()
 {

 }
 public int getEmployeeId() {
  return employeeId;
 }
 public void setEmployeeId(int employeeId) {
  this.employeeId = employeeId;
 }
 public String getEmployeeName() {
  return employeeName;
 }
 public void setEmployeeName(String employeeName) {
  this.employeeName = employeeName;
 }
 public String getDepartment() {
  return department;
 }
 public void setDepartment(String department) {
  this.department = department;
 }


 public String getNationality() {
  return nationality;
 }
 public void setNationality(String nationality) {
  this.nationality = nationality;
 }
 @Override
 public void readExternal(ObjectInput in) throws IOException,
 ClassNotFoundException {
  employeeId=in.readInt();
  employeeName=(String) in.readObject();
  
 }
 @Override
 public void writeExternal(ObjectOutput out) throws IOException {

  out.writeInt(employeeId);
  out.writeObject(employeeName);
 }
}
you must have no args contructor if you implement externalizable.
Create ExternalizableMain.java in org.arpit.java2blog
ExternalizableMain.java:
package org.arpit.java2blog;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ExternalizableMain {
 /**
  * @author Arpit Mandliya
  */
 public static void main(String[] args) {

  Employee emp = new Employee();
  emp.setEmployeeId(101);
  emp.setEmployeeName("Arpit");
  emp.setDepartment("CS");
  
  //Serialize
  try
  {
   FileOutputStream fileOut = new FileOutputStream("employee.ser");
   ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
   outStream.writeObject(emp);
   outStream.close();
   fileOut.close();
  }catch(IOException i)
  {
   i.printStackTrace();
  }

  //Deserialize
  emp = null;
  try
  {
   FileInputStream fileIn =new FileInputStream("employee.ser");
   ObjectInputStream in = new ObjectInputStream(fileIn);
   emp = (Employee) in.readObject();
   in.close();
   fileIn.close();
  }catch(IOException i)
  {
   i.printStackTrace();
   return;
  }catch(ClassNotFoundException c)
  {
   System.out.println("Employee class not found");
   c.printStackTrace();
   return;
  }
  System.out.println("Deserialized Employee...");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getEmployeeName());
 
 }
}

Run it :
When you run ExternalizableMain.java.You will get following output
Deserialized Employee...
Emp id: 101
Name: Arpit

If you already have serializable,why you need externalizable at all!!:

  • When you serialize any object using serializable, apart from fields, all objects that belong to object map and that can be reached using instance variable will also be serialized .for example :
    • If you have Employee class and its superclass is person then it will serialize all superclass objects (such as person) until it reaches "Object" class.
    • Similarly if Employee has instance variable of address class then it will serialize whole object map of address also .
           Do you really want this much overhead when all you want to serialize is employeeId and  employeeName
  • JVM uses reflection when you use serializable which is quite slow.
  • While serializing,information about class description which incluses description of its superclass and instance variable associated with that class also get stored in stream.Again this is also a performance issue

Inheritance in Externalization:

Now we will see how inheritance affects externalization.So there can be muliple cases whether super class is externalizable or not.If not then how will you handle that and how it works.Lets see by example.
We will create Person.java which will be superclass of Employee.

Case 1: What if super class does not implement Externalizable:

If superclass does not implements externalizable , you need to serialize superclass 's fields in subclass that implements Externalizable.

Person.java
package org.arpit.java2blog;
public class Person {
 
 String name="default";
 String nationality;
 
 public Person()
 {
  System.out.println("Person:Constructor");
 }

 public Person(String name, String nationality) {
  super();
  this.name = name;
  this.nationality = nationality;
 }
 
 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getNationality() {
  return nationality;
 }

 public void setNationality(String nationality) {
  this.nationality = nationality;
 }

}

Create Employee.java in org.arpit.java2blog
Employee.java:
package org.arpit.java2blog;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
;

public class Employee extends Person implements Externalizable{ 

 int employeeId;
 String department;
 
 public Employee()
 {
  
 }
 public Employee(int employeeId,String name,String department,String nationality)
 {
  super(name,nationality);
  this.employeeId=employeeId;
  this.department=department;
  System.out.println("Employee:Constructor");
 }
 
 public int getEmployeeId() {
  return employeeId;
 }
 public void setEmployeeId(int employeeId) {
  this.employeeId = employeeId;
 }
 
 public String getDepartment() {
  return department;
 }
 public void setDepartment(String department) {
  this.department = department;
 }



@Override
public void writeExternal(ObjectOutput out) throws IOException {
 
 /*since superclass does not implement externalizable, you need to serialize super class field in this class itself*/
 //superclass fields
 out.writeObject(name);
 out.writeObject(nationality);
 
 // its own fields
 out.writeInt(employeeId);
 out.writeObject(department);
}

@Override
public void readExternal(ObjectInput in) throws IOException,
  ClassNotFoundException {
 /*since superclass does not implement externalizable, you need to deserialize super class field in this class itself*/
 //superclass fields
 name=(String) in.readObject();
 nationality=(String) in.readObject();
 
 // its own fields
 employeeId=in.readInt();
 department=(String) in.readObject();

}
}
Create ExternalizableMain.java in org.arpit.java2blog
ExternalizableMain.java:
package org.arpit.java2blog;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ExternalizableMain {

 /**
  * @author Arpit Mandliya
  */
 public static void main(String[] args) {

  //Serialize
  Employee emp = new Employee(101,"Arpit","CS","Indian");
  System.out.println("Before serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
  System.out.println("************");
  System.out.println("Serializing");
  try
  {
   FileOutputStream fileOut = new FileOutputStream("employee.ser");
   ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
   outStream.writeObject(emp);
   outStream.close();
   fileOut.close();
  }catch(IOException i)
  {
   i.printStackTrace();
  }

  //Deserialize
  System.out.println("************");
  System.out.println("Deserializing");
  emp = null;
  try
  {
   FileInputStream fileIn =new FileInputStream("employee.ser");
   ObjectInputStream in = new ObjectInputStream(fileIn);
   emp = (Employee) in.readObject();
   in.close();
   fileIn.close();
  }catch(IOException i)
  {
   i.printStackTrace();
   return;
  }catch(ClassNotFoundException c)
  {
   System.out.println("Employee class not found");
   c.printStackTrace();
   return;
  }
  System.out.println("After serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
 }
} 
Run it :
When you run ExternalizableMain.java.You will get following output:
Employee:Constructor
Before serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian
************
Serializing
************
Deserializing
Person:Constructor
After serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian

Case 2: What if super class implements Externalizable:

If superclass implements externalizable ,then it will also have readExternal() and writeExternal() method so it will serialize its own fields in these methods

Person.java
package org.arpit.java2blog;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Person implements Externalizable{

 String name="default";
 String nationality;

 public Person()
 {
  System.out.println("Person:Constructor");
 }

 public Person(String name, String nationality) {
  super();
  this.name = name;
  this.nationality = nationality;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getNationality() {
  return nationality;
 }

 public void setNationality(String nationality) {
  this.nationality = nationality;
 }


 @Override
 public void writeExternal(ObjectOutput out) throws IOException {

  out.writeObject(name);
  out.writeObject(nationality);
 }

 @Override
 public void readExternal(ObjectInput in) throws IOException,
 ClassNotFoundException {
  name=(String) in.readObject();
  nationality=(String) in.readObject();

 }

}

Create Employee.java in org.arpit.java2blog
Employee.java:
package org.arpit.java2blog;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
;

public class Employee extends Person implements Externalizable{ 

 int employeeId;
 String department;

 public Employee()
 {

 }
 public Employee(int employeeId,String name,String department,String nationality)
 {
  super(name,nationality);
  this.employeeId=employeeId;
  this.department=department;
  System.out.println("Employee:Constructor");
 }

 public int getEmployeeId() {
  return employeeId;
 }
 public void setEmployeeId(int employeeId) {
  this.employeeId = employeeId;
 }

 public String getDepartment() {
  return department;
 }
 public void setDepartment(String department) {
  this.department = department;
 }



 @Override
 public void writeExternal(ObjectOutput out) throws IOException {

  super.writeExternal(out);
  out.writeInt(employeeId);
  out.writeObject(department);
 }

 @Override
 public void readExternal(ObjectInput in) throws IOException,
 ClassNotFoundException {

  super.readExternal(in);
  employeeId=in.readInt();
  department=(String) in.readObject();

 }
}
Create ExternalizableMain.java in org.arpit.java2blog
ExternalizableMain.java:
package org.arpit.java2blog;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ExternalizableMain {

 /**
  * @author Arpit Mandliya
  */
 public static void main(String[] args) {

  //Serialize
  Employee emp = new Employee(101,"Arpit","CS","Indian");
  System.out.println("Before serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
  System.out.println("************");
  System.out.println("Serializing");
  try
  {
   FileOutputStream fileOut = new FileOutputStream("employee.ser");
   ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
   outStream.writeObject(emp);
   outStream.close();
   fileOut.close();
  }catch(IOException i)
  {
   i.printStackTrace();
  }

  //Deserialize
  System.out.println("************");
  System.out.println("Deserializing");
  emp = null;
  try
  {
   FileInputStream fileIn =new FileInputStream("employee.ser");
   ObjectInputStream in = new ObjectInputStream(fileIn);
   emp = (Employee) in.readObject();
   in.close();
   fileIn.close();
  }catch(IOException i)
  {
   i.printStackTrace();
   return;
  }catch(ClassNotFoundException c)
  {
   System.out.println("Employee class not found");
   c.printStackTrace();
   return;
  }
  System.out.println("After serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
 }
} 

Run it :
When you run ExternalizableMain.java.You will get following output:
Employee:Constructor
Before serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian
************
Serializing
************
Deserializing
Person:Constructor
After serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian

In this example, since the Person class stores and restores its fields in its own writeExternal and readExternal methods, you dont need to save/restore the superclass fields in sub class but if you observe closely the writeExternal and readExternal methods of Employee class, you will find that you still need to first call the super.xxxx() methods that confirms the statement the externalizable object must also coordinate with its supertype to save and restore its state.

DownSides of Externalizable:

  • If you make any change to your class definition, you need to maintain writeExternal() and readExternal accordingly.
  • As we have seen in example,Sub class object has to coordinate with its superclass to save and store its state(by call super.xxxx() method from subclass)

Java HashMap:

    HashMap in java How HashMap works in java hash and indexfor method in HashMap hashcode and equals method in java How to sort HashMap by keys and values Difference between HashMap and HashSet Difference between HashMap and Hashtable How to iterate over HashMap
Target AudienceThis post is for the people who already have good understanding of how HashMap works in java and want to understand more about hash and indexFor method.

In this post, we will see how hash and indexFor method works internally in hashmap. hash and indexFor methods belong to HashMap class. Why JDK developers need to have another hash function when  key object's have there own hashcode method.
Lets see code for hash and indexFor
/**
 * Applies a supplemental hash function to a given hashCode, which
 * defends against poor quality hash functions.  This is critical
 * because HashMap uses power-of-two length hash tables, that
 * otherwise encounter collisions for hashCodes that do not differ
 * in lower bits. Note: Null keys always map to hash 0, thus index 0.
 */
static int hash(int h) {
    // 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);
}
 
/**
 * Returns index for hash code h.
 */
static int indexFor(int h, int length) {
    return h & (length-1);
}
As java doc says about hash:
 Applies a supplemental hash function to a given hashCode, which defends against poor quality hash functions. This is critical because HashMap uses power-of-two length hash tables, that otherwise encounter collisions for hashCodes that do not differ in lower bits. Note: Null keys always map to hash 0, thus index 0.

Lets understand this with the help of example: Lets say key object's hashcode() returns only 3 values 31,63 and 95. 31,63 and 95 are integers, so all are 32 bits.
 31=00000000000000000000000000011111
 63=00000000000000000000000000111111
 95=00000000000000000000000001011111
 Now lets say our hashmap's lenth is 16(2^4, Hashmap's length will be always in power of two)

What if we do not use hash function: 

Now if we do not use hash function then 
indexFor will return :
 31=00000000000000000000000000011111 => 1111=15
 63=00000000000000000000000000111111  => 1111=15
 95=00000000000000000000000001011111 => 1111=15

Why so?
because when we call indexFor function.It will do AND between 31&15 , 63&15 and 95&15.
For ex. 95&15
00000000000000000000000001011111 &00000000000000000000000000001111
so (2^n-1) will always have sequence of 1's in it.so what matters here it last n bits as 0&1 is always 0.
In above example, all have 1111 in end that is why they are returning same index.
so although we have different hashcode,each Entry object will be stored at 15 index only.

What if we  use hash function:

If we use hash function then :
On each hashcode, first hash will  be applied.
 31=00000000000000000000000000011111 => 00000000000000000000000000011110
 63=00000000000000000000000000111111  => 00000000000000000000000000111100
 95=00000000000000000000000001011111 => 00000000000000000000000001011010

 now after passing new hash to indexFor.It will return :
00000000000000000000000000011110 =>1110=14
00000000000000000000000000111100 =>1100=12
00000000000000000000000001011010 =>1010=10

After applying hash function, above hashcodes are returning different index so hash function is redistributing elements in hashmap thus less collisions and better performance.
The main purpose of hash operation is to make the hashcode differences visible in the least significant bits so that the hashmap elements can be distributed evenly across the buckets

Note:

  • If two key objects have same hashcode, then they will always go to same index in table array
  • If two key objects do not have same hashcode then they may or may not go to same index in table array.

Java HashMap:

    HashMap in java How HashMap works in java hash and indexfor method in HashMap hashcode and equals method in java How to sort HashMap by keys and values Difference between HashMap and HashSet Difference between HashMap and Hashtable How to iterate over HashMap
Most common interview questions are "How HashMap works in java", "How get and put method of HashMap work internally". Here I am trying to explain internal functionality with an easy example. Rather than going through theory, we will start with example first, so that you will get better understanding and then we will see how get and put function work in java.


Lets take a very simple example. I have a Country class, we are going to use Country class object as key and its capital name(string) as value. Below example will help you to understand, how these key value pair will be stored in hashmap.

1. Country.java 
package org.arpit.java2blog;
public class Country {

 String name;
 long population;
 
 public Country(String name, long population) {
  super();
  this.name = name;
  this.population = population;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public long getPopulation() {
  return population;
 }
 public void setPopulation(long population) {
  this.population = population;
 }
 
 // If length of name in country object is even then return 31(any random number) and if odd then return 95(any random number).
 // This is not a good practice to generate hashcode as below method but I am doing so to give better and easy understanding of hashmap.
 @Override
 public int hashCode() {
  if(this.name.length()%2==0)
   return 31;
  else 
   return 95;
 }
 @Override
 public boolean equals(Object obj) {
  
  Country other = (Country) obj;
   if (name.equalsIgnoreCase((other.name)))
   return true;
  return false;
 }
  
}

If you want to understand more about hashcode and equals method of object, you may refer hashcode() and equals() method in java

2. HashMapStructure.java(main class)

import java.util.HashMap;
import java.util.Iterator;
  
public class HashMapStructure {
  
    /**
     * @author Arpit Mandliya
     */
    public static void main(String[] args) {
          
        Country india=new Country("India",1000);
        Country japan=new Country("Japan",10000);
          
        Country france=new Country("France",2000);
        Country russia=new Country("Russia",20000);
          
       HashMap<Country, String> countryCapitalMap=new HashMap<Country,String>();  
        countryCapitalMap.put(india,"Delhi");  
        countryCapitalMap.put(japan,"Tokyo");  
        countryCapitalMap.put(france,"Paris");  
        countryCapitalMap.put(russia,"Moscow");  
            
        Iterator<Country> countryCapitalIter=countryCapitalMap.keySet().iterator();//put debug point at this line  
        while(countryCapitalIter.hasNext())  
        {  
            Country countryObj=countryCapitalIter.next();  
            String capital=countryCapitalMap.get(countryObj);  
            System.out.println(countryObj.getName()+"----"+capital);  
         }  
        }  
  
  
} 
Now put debug point at line 23 and right click on project->debug as-> java application. Program will stop execution at line 23 then right click on countryCapitalMap then select watch.You will be able to see structure as below.


Now From above diagram, you can observe following points
  1. There is an Entry[] array called table which has size 16.
  2. This table stores Entry class's object. HashMap class has a inner class called Entry.This Entry have key value as instance variable. Lets see structure of entry class Entry Structure.
  3. static class Entry implements Map.Entry
    {
            final K key;
            V value;
            Entry next;
            final int hash;
            ...//More code goes here
    } 
  4. Whenever we try to put any key value pair in hashmap, Entry class object is instantiated for key value and that object will be stored in above mentioned Entry[](table). Now you must be wondering, where will above created Enrty object get stored(exact position in table). The answer  is, hash code is calculated for a key by calling Hascode() method. This hashcode is used to calculate index for above Entry[] table.
  5. Now, If you see at array index 10 in above diagram, It has an Entry object named HashMap$Entry.
  6. We have put 4 key-values in hashmap but it seems to have only 2!!!!This is because if two objects have same hashcode, they will be stored at same index. Now question arises how? It stores objects in a form of LinkedList(logically).
So how hashcode of above country key-value pairs are calculated. 
Hashcode for Japan = 95 as its length is odd.
Hashcode for India =95 as its length is odd
HashCode for Russia=31 as its length is even.
HashCode for France=31 as its length is even.

Below diagram will explain LinkedList concept clearly.


 So now if you have good understanding of hashmap structure,Lets go through put and get method.

Put :

Lets see implementation of put method:
/**
  * Associates the specified value with the specified key in this map. If the
  * map previously contained a mapping for the key, the old value is
  * replaced.
  *
  * @param key
  *            key with which the specified value is to be associated
  * @param value
  *            value to be associated with the specified key
  * @return the previous value associated with <tt>key</tt>, or <tt>null</tt>
  *         if there was no mapping for <tt>key</tt>. (A <tt>null</tt> return
  *         can also indicate that the map previously associated
  *         <tt>null</tt> with <tt>key</tt>.)
  */
 public V put(K key, V value) {
  if (key == null)
   return putForNullKey(value);
  int hash = hash(key.hashCode());
  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;
 } 
now lets understand above code step by step
  1. Key object is checked for null. If key is null then it will be stored at table[0] because hashcode for null is always 0.
  2. Key object's hashcode() method is called and hash code is calculated. This hashcode is used to find index of array for storing Entry object. It may happen sometimes that, this hashcode function is poorly written so JDK designer has put another function called hash() which takes above calculated hash value as argument.If you want to learn more about hash() function, you can refer hash and indexFor method in hashmap.
  3. indexFor(hash,table.length)  is used to calculate exact index in table array for storing the Entry object.
  4. As we have seen in our example, if two key objects have same hashcode(which is known as collision) then it will be stored in form of linkedlist.So here, we will iterate through our linkedlist.
  • If there is no element present at that index which we have just calculated then it will directly put our Entry object at that index.
  • If There is element present at that index then it will iterate until it gets Entry->next as null.Then current Entry object become next node in that linkedlist
  • What if we are putting same key again, logically it should replace old value. Yes,it will do that.While iterating it will check key equality by calling equals() method(key.equals(k)), if this method returns true then it replaces value object with current Entry's value object.

 Get:

 Lets see implementation of get now:
/**
  * Returns the value to which the specified key is mapped, or {@code null}
  * if this map contains no mapping for the key.
  *
  * <p>
  * More formally, if this map contains a mapping from a key {@code k} to a
  * value {@code v} such that {@code (key==null ? k==null :
  * key.equals(k))}, then this method returns {@code v}; otherwise it returns
  * {@code null}. (There can be at most one such mapping.)
  *
  * </p><p>
  * A return value of {@code null} does not <i>necessarily</i> indicate that
  * the map contains no mapping for the key; it's also possible that the map
  * explicitly maps the key to {@code null}. The {@link #containsKey
  * containsKey} operation may be used to distinguish these two cases.
  *
  * @see #put(Object, Object)
  */
 public V get(Object key) {
  if (key == null)
   return getForNullKey();
  int hash = hash(key.hashCode());
  for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
   Object k;
   if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
    return e.value;
  }
  return null;
 } 
As you got the understanding on put functionality of hashmap. So to understand get functionality is quite simple. If you pass any key to get value object from hashmap.
  1. Key object is checked for null. If key is null then value of Object resides at table[0] will be returned.
  2. Key object's hashcode() method is called and hash code is calculated.
  3. indexFor(hash,table.length)  is used to calculate exact index in table array using generated hashcode for getting the Entry object.
  4. After getting index in table array, it will iterate through linkedlist and check for key equality by calling equals() method and if it returns true then it returns the value of Entry object else returns null.

Key points to Remeber:

  • HashMap has a inner class called Entry which stores key-value pairs.
  • Above Entry object is stored in Entry[ ](Array) called table
  • An index of table is logically known as bucket and it stores first element of linkedlist
  • Key object's hashcode() is used to find bucket of that Entry object.
  • If two key object 's have same hashcode , they will go in same bucket of table array.
  • Key object 's equals() method is used to ensure uniqueness of key object.
  • Value object  's equals() and hashcode() method is not used at all
Please go through  core java interview questions for more interview questions.

In this post ,we will try to understand hashcode() and equals() method in java.

Java HashMap:

    HashMap in java How HashMap works in java hash and indexfor method in HashMap hashcode and equals method in java How to sort HashMap by keys and values Difference between HashMap and HashSet Difference between HashMap and Hashtable How to iterate over HashMap
These methods can be found in the Object class and hence available to all java classes.Using these two methods, an object can be stored or retrieved from a Hashtable, HashMap or HashSet.
  • hashcode()
  • equals()

hashcode() and equals() method

hashcode():
You might know if you put entry in HashMap, first hashcode is calculated and this hashcode used to find bucket(index) where this entry will get stored in hashMap.You can read more at How hashMap works in java. What if you don't override hashcode method, it will  return integer representation of memory address.

equals():
You have to override equals method, when you want to define equality between two object. If you don't override this method, it will check for reference equality(==) i.e. if tow reference refers to same object or not

Lets override default implemenation of hashcode() and equals():

You don't have to always override these methods, but lets say you want to define equality of country object based on name, then you need to override equals method and if you are overriding equals method, you should override hashcode method too. Below example will make it clear.

Lets see with the help of example.We have a class called Country

1. Country.java
package org.arpit.java2blog;

public class Country {

    String name;
    long population;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public long getPopulation() {
        return population;
    }
    public void setPopulation(long population) {
        this.population = population;
    }
    
}

This country class have two basic attributes- name and population.

Now create a class called "EqualityCheckMain.java"

package org.arpit.java2blog;

public class EqualityCheckMain {

    /**
     * @author arpit mandliya
     */
    public static void main(String[] args) {
        
        Country india1=new Country();
        india1.setName("India");
        Country india2=new Country();
        india2.setName("India");
        System.out.println("Is india1 is equal to india2:" +india1.equals(india2));
    }

}
When you run above program, you will get following output
Is india1 is equal to india2:false
In above program, we have created two different objects and set their name attribute to "india".
Because both references india1 and india2 are pointing to different object, as default implementation of equals check for ==,equals method is returning false. In real life, it should have return true because no two countries can have same name.

Now lets override equals and return true if two country's name are same.

Add this method to above country class:

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Country other = (Country) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

and now run EqualityCheckMain.java again
You will get following output:
Is india1 is equal to india2:true

Now this is because overriden equals method return true if two country have same name.
One thing to remember here, signature of equals method should be same as above.

Lets put this Country objects in hashmap:

Here we are going to use Country class object as key and its capital name(string) as value in HashMap.

package org.arpit.java2blog;

import java.util.HashMap;
import java.util.Iterator;

public class HashMapEqualityCheckMain {

    /**
     * @author Arpit Mandliya
     */
    public static void main(String[] args) {
        HashMap<Country,String> countryCapitalMap=new HashMap<Country,String>(); 
        Country india1=new Country();
        india1.setName("India");
        Country india2=new Country();
        india2.setName("India");

        countryCapitalMap.put(india1, "Delhi");
        countryCapitalMap.put(india2, "Delhi");

        Iterator<Country> countryCapitalIter=countryCapitalMap.keySet().iterator();
        while(countryCapitalIter.hasNext())
        {
            Country countryObj=countryCapitalIter.next();
            String capital=countryCapitalMap.get(countryObj);
            System.out.println("Capital of "+ countryObj.getName()+"----"+capital);

        }
    } 
}

When you run above program, you will see following output:
Capital of India----Delhi
Capital of India----Delhi

Now you must be wondering even through two objects are equal why HashMap contains two key value pair instead of one.This is because First HashMap uses hashcode to find bucket for that key object, if hashcodes are same then only it checks for equals method and because hashcode for above two country objects uses default hashcode method,Both will have different memory address hence different hashcode.

Now lets override hashcode method.Add following method to Country class

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

Now run HashMapEqualityCheckMain.java again
You will see following output:
Capital of India----Delhi

So now hashcode for above two objects india1 and india2 are same, so Both will be point to same bucket,now equals method will be used to compare them which  will return true.
This is the reason java doc says "if you override equals() method then you must override hashCode() method"

hashcode() and equals() contracts: 

equals():

The equals method implements an equivalence relation on non-null object references:
  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

hashcode():

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

Key points to remember:

  1. If you are overriding equals method then you should override hashcode() also.
  2. If two objects are equal then they must have same hashcode.
  3. If two objects have same hashcode then they may or may not be equal
  4. Always use same attributes to generate equals and hashcode as in our case we have used name.
Please go through  core java interview questions for experienced for more interview questions.
 

Java tutorial for beginners Copyright © 2012