Java – HashMap in-detail explanation

HashMap works based on hashing algorithm, As per Java doc HashMap has below four constructors,
ConstructorDescription
HashMap​()Constructs an empty
HashMap with the default initial capacity (16) and the default load factor (0.75).
HashMap​(int initialCapacity)Constructs an empty
HashMap with the specified initial capacity and the default load factor (0.75).
HashMap​(int initialCapacity,
float loadFactor)
Constructs an empty
HashMap with the specified initial capacity and load factor.
HashMap​(Map<? extends K,? extends V> m)Constructs a new
HashMap with the same mappings as the specified
Map.
Let’s write simple java program, to examine how Map works internally
  1. Create a simple Map and add one key and value to it
1public static void main(String[] args) {
2 
3Map<Integer, String> map = new HashMap<>();
4 
5map.put(1"Java");
6 
7}
We just created Simple Map, which takes key as Integer and Value as String, and added “1” as Key and “Java” as value. By using eclipse debug feature, lets see what’s inside the map
It created 16 blocks(0-15) and inserted 1st block with key as Integer “1” and Value as String “Java”. Please check the red box, rest all boxes initialized with  null.
2. Add second key and value to the same map
1public static void main(String[] args) {
2 
3Map<Integer, String> map = new HashMap<>();
4 
5map.put(1"Java");
6 
7map.put(2"Angular");
8 
9}
lets see the map in eclipse debug again
Now the map contains two keys (1,2) and two values (“Java”, “Angular”) as expected, but the keys are added exactly at 1st block and 2nd block respectively, why?
because as we know Map works based on hashing algorithm, whenever we insert key to map, it calls the Object#hashcode() method, based on the value of hashCode(), it will insert the key into that block.
In above case, Integer class overrides the hashCode with its primitive int value, thats why (1,java) got stored in 1st block and (2,Angular) got store in 2nd block.
3. Lets do the same experiment with our own Class
Create a simple Employee class like below
1private static class Employee{
2int id;
3String name;
4 
5Employee(int id, String name){
6this.id = id;
7this.name = name;
8}
9}
Use this class as Key to the map and examine the same way
1public static void main(String[] args) {
2Map<Employee, String> map = new HashMap<>(10);
3map.put(new Employee(1"Ramesh"), "Java");
4map.put(new Employee(2"Sathish"), "Angular");
5}
We have added two keys as Employee objects and Values as just strings, lets see in which block the keys got stored this time
This time, it stored in 8th block and 14th block(why? simple answer because of hashCode of Employee objects), to confirm this, lets override hashCode() of Employee to constant value and check the map. If our analysis correct it has to store all the key’s in the same block.
Update Employee class accordingly
01private static class Employee{
02int id;
03String name;
04Employee(int id, String name){
05this.id = id;
06this.name = name;
07}
08@Override
09public int hashCode() {
10return 10;
11}
12}
We dont need to change anything to our map, lets see now where the keys got stored
Yes, only 10th block got filled with two objects, why? because both employee objects returned the same hashCode (i.e 10). But how does Map recognized those two objects are not duplicate? As we know internally Map#Key is an entrySet(java.util.Set) it called equals method to verify whether the key is duplicate or not.
While retrieving the value from Map also, first it will check the hashCode of the given key and based on that it will go to that block, after finding the block it will call equals() to get the exact value.
So overriding the hashCode() to constant is not at all recommendable. and when we override the hashCode() we should not forget to override the equals() method as well(i.e hashCode()/equals() contract).

Comments