GETTING STARTED WITH HASHMAPS


Java HashMaps are a powerful Key/value pair container. Using a unique key they provide fast access the the value associated with that key. In this tutorial I'll cover a basic usage example, how to iterate, check if a key value exists and how to create your own objects to act as a key (or value). The basics covered here are intended to provide a starting point for using Java collections.
HashMaps contain a list of key/value pairs. The type of objects stored as the key and value are declared when the collection is created. This is an example of Java generics and provides compile time checking to ensure you only add the allowed type of objects to the HashMap.
Map<String,String> example = new HashMap<String,String>();

// Adding some values to the HashMap

example.put( "Wayne", new String( "Rooney" ));

example.put( "Alan", new String( "Shearer" ));

example.put( "Rio", new String( "Ferdinand" ));

example.put( "John", new String( "Terry" ));
// Find out how many key/value pairs the HashMap contains

System.out.println("The HashMap contains " + example.size() + " pairs"Line 2. creates a new HashMap object and sets the key and value object types to String objects. Notice that the HashMap implements the Map<K,V> interface.
Line 5. shows the use of the .put() method to add the key and value objects as a new pair.
Line 11. prints the size of the HashMap to the console. In this case it will show that it contains 4 key/value pairs.
Checking for a key

Checking if a HashMap contains a specific key is a fast operation when compared to a linear search through a list such as a Vector.

String newKey = "Ian";
if ( !example.containsKey( newKey ) ) {
example.put( newKey, new String( "Wright" ) );

}
This example checks if the HashMap contains a specific key. If the key is not contained in the Map a new key/value pair are added.
HashMap iterators

There are a number of ways to iterate through the pairs in a HashMap.

for (String key : example.keySet() ) {
// Get the String value that goes with the key
String value = example.get( key );
// Print the key and value
System.out.println( key + " = " + value);
}
//The method shown above is available in Java 1.5 onwards. It is a much easier to read (and code) way of iterating through a set of objects. If it were to be run it would produce the following output.

Wayne = Rooney

Alan = Shearer

Rio = Ferdinand

John = Terry

Ian = Wright
Custom key objects

//Any object can be used for the key or value in a Map. Two methods must be implemented for this to work properly however: the hashCode() and equals() methods. An example of this might be the following.

public class SoccerPlayer {

private String firstname;

private String surname;

public SoccerPlayer(String firstname, String surname)

this.firstname = firstname;

this.surname = surname;

}

public String toString() {

return surname + ", " + firstname;

}

public int hashCode() {

return this.toString().hashCode();

}

public boolean equals(Object player) {
// Check the object is valid and is of the correct class type

if ( player == null ) return false;

if ( this.getClass() != player.getClass() ) return false;
// It is now safe to cast to SoccerPlayer and compare the classes data members

String name = ((SoccerPlayer)player).toString();

return this.toString().equals(name);

}

}
Remember that keys in a HashMap must be unique, so using enough information from within your object to create a unique hashcode is important. In this case, if another object contained the same firstname and surname, it would over-write the first key value, but that would be ok since I can be sure they are the same. If the example contained amiddlename variable, but the hashcode did not take this into account there might be a situation where the hashcodes for two different keys are the same.
The equals() method overrides the default implementation in Object. The equals() method in Object only compares the objects references, known as a shallow comparison, since Object does not contain any data members of its own. The implementation of equals() you provide, as like the one above must compare the data members of the class to ensure equality. This is called a deep comparison.

HashMap examples


HashMap1 Example Code





import com.sfdc.sss.*;

import java.util.Enumeration;



/**

 * Construction, enumeration, access, rejection of duplicates.

 */



public class HashMap1

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap();

    map.add( new Integer( 2 ), "two" );

    map.add( new Integer( 4 ), "four" );

    System.out.println( map );

    System.out.println();



    System.out.println( "Enumerate the HashMap" );

    Enumeration e = map.elements();

    while ( e.hasMoreElements() )

      System.out.println( e.nextElement() );

    System.out.println();



    System.out.println( "Iterate through the HashMap" );

    for ( HashMapIterator i = map.begin(); !i.atEnd(); i.advance() )

      System.out.println( i.get() + ", key = " + i.key() + ", value = " + i.value() );

    System.out.println();



    System.out.println( "Demonstrate access" );

    System.out.println( "map.get( 2 ) = " + map.get( new Integer( 2 ) ) );

    System.out.println( "map.get( 5 ) = " + map.get( new Integer( 5 ) ) );

    System.out.println( "map = " + map );

    System.out.println();



    System.out.println( "Show that duplicates cannot be added." );

    Object value = map.add( new Integer( 8 ), "eight" );

    if ( value != null )

      System.out.println( "Could not add 8." );

    else

      System.out.println( "Added 8." );

    System.out.println( "map = " + map );



    value = map.add( new Integer( 4 ), "FOUR" );

    if ( value != null )

      System.out.println( "Could not add 4." );

    else

      System.out.println( "Added 4." );

    System.out.println( "map = " + map );

    System.out.println();



    System.out.println( "Demonstrate modification" );

    map.put( new Integer( 4 ), "FOUR" );

    System.out.println( "map = " + map );

    }

  }

HashMap1 Example Output





HashMap( Pair( 2, two ), Pair( 4, four ) )



Enumerate the HashMap

two

four



Iterate through the HashMap

Pair( 2, two ), key = 2, value = two

Pair( 4, four ), key = 4, value = four



Demonstrate access

map.get( 2 ) = two

map.get( 5 ) = null

map = HashMap( Pair( 2, two ), Pair( 4, four ) )



Show that duplicates cannot be added.

Added 8.

map = HashMap( Pair( 2, two ), Pair( 4, four ), Pair( 8, eight ) )

Could not add 4.

map = HashMap( Pair( 2, two ), Pair( 4, four ), Pair( 8, eight ) )



Demonstrate modification

map = HashMap( Pair( 2, two ), Pair( 4, FOUR ), Pair( 8, eight ) )



HashMap2 Example Code



//

import com.sfdc.sss.*;

import java.util.Enumeration;



/**

 * Accessing keys and values.

 */



public class HashMap2

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap();

    map.add( "cat", "Meow" );

    map.add( "ape", "Squeak" );

    map.add( "dog", "Woof" );

    map.add( "bat", "Squeak" );

    System.out.println( "map = " + map );



    System.out.print( "Enumerate the HashMap: " );

    Enumeration e = map.elements();

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " ");

    System.out.println();



    System.out.print( "map.keys() = " );

    e = map.keys();

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " ");

    System.out.println();



    System.out.print( "map.keys( Squeak ) = " );

    e = map.keys( "Squeak" );

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " ");

    System.out.println();



    System.out.print( "map.values( bat ) = " );

    e = map.values( "bat" );

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " ");

    System.out.println();

    }

  }

HashMap2 Example Output





map = HashMap( Pair( dog, Woof ), Pair( ape, Squeak ), Pair( bat, Squeak ), Pair( cat, Meow ) )

Enumerate the HashMap: Woof Squeak Squeak Meow

map.keys() = dog ape bat cat

map.keys( Squeak ) = ape bat

map.values( bat ) = Squeak



HashMap3 Example Code



//

import com.sfdc.sss.*;



/**

 * Counting, finding, erasing.
 */



public class HashMap3

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap();

    map.add( "cat", "Meow" );

    map.add( "ape", "Squeak" );

    map.add( "dog", "Woof" );

    map.add( "bat", "Squeak" );

    System.out.println( map );

    System.out.println( "map.count( dog ) = " + map.count( "dog" ) );

    HashMapIterator i = map.find( "dog" );

    if ( i.equals( map.end() ) ) // A simpler way: if ( i.atEnd() ) ...

      System.out.println( "Could not find dog." );

    else

      System.out.println( "Found " + i.get() );

    System.out.println( "map.remove( dog ) = " + map.remove( "dog" ) );

    HashMapIterator j = map.find( "dog" );

    if ( j.atEnd() ) // A simpler way: if ( j.equals( map.end() ) ) ...

      System.out.println( "Could not find dog." );

    else

      System.out.println( "Found " + j.get() );

    }

  }

HashMap3 Example Output





HashMap( Pair( dog, Woof ), Pair( ape, Squeak ), Pair( bat, Squeak ), Pair( cat, Meow ) )

map.count( dog ) = 1

Found Pair( dog, Woof )

map.remove( dog ) = Woof

Could not find dog.



HashMap4 Example Code



//

import com.sfdc.jgl.*;

import java.util.Enumeration;



/**

 * Construction, enumeration, access, acceptance of duplicates.

 */



public class HashMap4

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap( true ); // allow duplicates

    map.add( new Integer( 2 ), "two" );

    map.add( new Integer( 4 ), "four" );

    System.out.println( map );

    System.out.println();



    System.out.println( "Enumerate the HashMap" );

    Enumeration e = map.elements();

    while ( e.hasMoreElements() )

      System.out.println( e.nextElement() );

    System.out.println();



    System.out.println( "Iterate through the HashMap" );

    for ( HashMapIterator i = map.begin(); !i.atEnd(); i.advance() )

      System.out.println( i.get() + ", key = " + i.key() + ", value = " + i.value() );

    System.out.println();



    System.out.println( "Show that duplicates can be added." );

    map.add( new Integer( 8 ), "eight" );

    System.out.println( "map = " + map );



    map.add( new Integer( 4 ), "FOUR" );

    System.out.println( "map = " + map );



    System.out.println( "Show that even with duplicates, put() does a replacement." );

    map.put( new Integer( 4 ), "FoUr" );

    System.out.println( "map = " + map );



    }

  }

HashMap4 Example Output





HashMap( Pair( 2, two ), Pair( 4, four ) )



Enumerate the HashMap

two

four



Iterate through the HashMap

Pair( 2, two ), key = 2, value = two

Pair( 4, four ), key = 4, value = four



Show that duplicates can be added.

map = HashMap( Pair( 2, two ), Pair( 4, four ), Pair( 8, eight ) )

map = HashMap( Pair( 2, two ), Pair( 4, four ), Pair( 4, FOUR ), Pair( 8, eight ) )

Show that even with duplicates, put() does a replacement.

map = HashMap( Pair( 2, two ), Pair( 4, FoUr ), Pair( 4, FOUR ), Pair( 8, eight ) )



HashMap5 Example Code



//

import com.sfdc.jgl.*;

import java.util.Enumeration;



/**

 * Accessing keys and values.

 */



public class HashMap5

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap( true ); // allow duplicates

    map.add( "cat", "Meow" );

    map.add( "ape", "Squeak" );

    map.add( "ape", "Whoop" );

    map.add( "bat", "Squeak" );

    System.out.println( "map = " + map );

    System.out.println();



    System.out.println( "Enumerate the HashMap" );

    Enumeration e = map.elements();

    while ( e.hasMoreElements() )

      System.out.println( e.nextElement() );

    System.out.println();



    e = map.keys();

    System.out.print( "map.keys() = " );

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " " );

    System.out.println();



    e = map.keys( "Squeak" );

    System.out.print( "map.keys( Squeak ) = " );

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " " );

    System.out.println();



    e = map.values( "ape" );

    System.out.print( "map.keys( ape ) = " );

    while ( e.hasMoreElements() )

      System.out.print( e.nextElement() + " " );

    System.out.println();

    }

  }

HashMap5 Example Output





map = HashMap( Pair( ape, Squeak ), Pair( ape, Whoop ), Pair( bat, Squeak ), Pair( cat, Meow ) )



Enumerate the HashMap

Squeak

Whoop

Squeak

Meow



map.keys() = ape ape bat cat

map.keys( Squeak ) = ape bat

map.keys( ape ) = Squeak Whoop



HashMap6 Example Code



//

import com.sfdc.jgl.*;



/**

 * Counting, finding, erasing.


 */



public class HashMap6

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap( true ); // allow duplicates

    map.add( "cat", "Meow" );

    map.add( "ape", "Squeak" );

    map.add( "ape", "Whoop" );

    map.add( "bat", "Squeak" );

    System.out.println( map );



    System.out.println( "map.count( ape ) = " + map.count( "ape" ) );

    HashMapIterator i = map.find( "ape" );



    if ( i.equals( map.end() ) ) // A simpler way: if ( i.atEnd() ) ...

      {

      System.out.println( "Could not find dog." );

      }

    else

      {

      while ( !i.atEnd() && i.key().equals( "ape" ) )

        {

        System.out.println( "Found " + i.get() );

        i.advance();

        }

      }

    System.out.println( "map.remove( ape ) = " + map.remove( "ape" ) );

    HashMapIterator j = map.find( "ape" );

    if ( j.atEnd() ) // A simpler way: if ( j.equals( map.end() ) ) ...

      System.out.println( "Could not find ape." );

    else

      System.out.println( "Found " + j.get() );

    }

  }

HashMap6 Example Output





HashMap( Pair( ape, Squeak ), Pair( ape, Whoop ), Pair( bat, Squeak ), Pair( cat, Meow ) )

map.count( ape ) = 2

Found Pair( ape, Squeak )

Found Pair( ape, Whoop )

map.remove( ape ) = Squeak

Could not find ape.



HashMap7 Example Code



//

import com.sfdc.jgl.*;



public class HashMap7

  {

  public static void main( String[] args )

    {

    HashMap map = new HashMap( true ); // allow duplicates

    map.add( new Integer( 3 ), "three" );

    map.add( new Integer( 8 ), "eight" );

    map.add( new Integer( 2 ), "two" );

    map.add( new Integer( 3 ), "THREE" );

    System.out.println( map );



    Range range = map.equalRange( new Integer( 3 ) );

    while ( !range.begin.equals( range.end ) )

      System.out.println( "match @ " + range.begin.nextElement() );

    }

  }

HashMap7 Example Output





HashMap( Pair( 2, two ), Pair( 3, three ), Pair( 3, THREE ), Pair( 8, eight ) )

match @ Pair( 3, three )

match @ Pair( 3, THREE )

Hashmap Internal Implementation Analysis in Java.


java.util.HashMap.java

/**
* The maximum capacity, used if a higher value is implicitly specified

* by either of the constructors with arguments.

* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 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;
}
/**
* 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);
}

1111110111011010101101010111110

1100110111011010101011010111110

1100000111011010101110010111110

1111110111011010101101010111110 & 0000000000000000000000000001111 = 1110


1100110111011010101011010111110 & 0000000000000000000000000001111 = 1110


1100000111011010101110010111110 & 0000000000000000000000000001111 = 1110

0000 - 0 1000 - 8
0001 - 1 1001 - 9
0010 - 2 1010 - 10
0011 - 3 1011 - 11
0100 - 4 1100 - 12
0101 - 5 1101 - 13
0110 - 6 1110 - 14
0111 - 7 1111 - 15

this is what happens inside indexFor method
1111110111011010101101010111110 ==> 1111001111110011100110111011010
1100110111011010101011010111110 ==> 1100000010010000101101110011001
1100000111011010101110010111110 ==> 1100110001001000011011110001011
passing these set of new hashcodes to indexFor() method


1111001111110011100110111011010 & 0000000000000000000000000001111 = 1010
1100000010010000101101110011001 & 0000000000000000000000000001111 = 1001
1100110001001000011011110001011 & 0000000000000000000000000001111 = 1011
/**
* 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;
}

/**
* 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>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());
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)))

return e.value;
}
return null;
}

It says the maximum size to which hashmap can expand, i.e, till 2^(30) = 1,073,741,824


java.util.HashMap.java


It says default size of an array is 16 (always power of 2, we will understand soon why it is always power of 2 going further) and load factor means whenever the size of the hashmap reaches to 75% of its current size, i.e, 12, it will double its size by recomputing the hashcodes of existing data structure elements.


Hence to avoid rehashing of the data structure as elements grow it is the best practice to explicitly give the size of the hashmap while creating it.


Do you foresee any problem with this resizing of hashmap in java? Since java is multi threaded it is very possible that more than one thread might be using same hashmap and then they both realize the need for re-sizing the hashmap at the same time which leads to race condition.


What is race condition with respect to hashmaps? When two or more threads see the need for resizing the same hashmap, they might end up adding the elements of old bucket to the new bucket simultaneously and hence might lead to infinite loops. FYI, in case of collision, i.e, when there are different keys with same same hashcode, internally we use single linked list to store the elements. And we store every new element at the head of the linked list to avoid tail traversing and hence at the time of resizing the entire sequence of objects in linked list gets reversed, during which there are chances of infinite loops.


For example, lets assume there are 3 keys with same hashcode and hence stored in linked list inside a bucket [below format is in object_value(current_address, next_address) ]

Initial structure: 1(100, 200) –> 2(200, 300) –> 3(300, null)

After resizing by thread-1: 3(300, 200) –> 2(200, 100) –> 1(100, null)

When thread-2 starts resizing, its again starts with 1st element by placing it at the head:

1(100, 300) –> 3(300, 200) –> 2(200, 100) ==> which becomes a infinite loop for next insertion and thread hangs here.

java.util.HashMap.java

Here it

1. re-generates the hashcode using hash(int h) method by passing user defined hashcode as an argument

2. generates index based on the re-generated hashcode and length of the data structure.

3. if key exists, it over-rides the element, else it will create a new entry in the hashmap at the index generated in Step-2

Step-3 is straight forward but Step-1&2 needs to have deeper understanding. Let us dive into the internals of these methods…

Note: These two methods are very very important to understand the internal working functionality of hashmap in openjdk

java.util.HashMap.java

here:

‘h’ is hashcode(because of its int data type, it is 32 bit)

‘length’ is DEFAULT_INITIAL_CAPACITY(because of its int data type, it is 32 bit)

Comment from above source code says…

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. What do this means???

It means that if in case the algorithm we wrote for hashcode generation does not distribute/mix lower bits evenly, it will lead to more collisions. For example, we have hashcode logic of “empId*deptId” and if deptId is even, it would always generate even hashcodes because any number multiplied by EVEN is always EVEN. And if we directly depend on these hashcodes to compute the index and store our objects into hashmap then

1. odd places in the hashmap are always empty

2. because of #1, it would leave us to use only even places and hence double the number of collisions

For example,

I am considering some hash codes which our code might generate, which are very valid as they are different, but we will prove these to be useless soon

I am considering these sequences directly (without using hash function) and pass it for indexFor method, where we do AND operation between ‘hashcode’ and ‘length-1(which will always give sequence of 1′s as length is always power of 2)’. Why is hashmap length always power of 2? it is because when we do (user_hash_code & size-1), it will consider only the first few bits (in this case 4 bits) which are used to decide the bucket location.

As we are considering the length as default length, i.e, 16, binary representation of (16-1) is 1111

this is what happens inside indexFor method

From this we understand that all the objects with these different hascodes would have same index which means they would all go into the same bucket, which is a BIG-FAIL as it leads to arraylist complexity O(n) instead of O(1)

Comment from above source code says…

that otherwise encounter collisions for hashCodes that do not differ in lower bits.

Notice this sequence of 0-15 (2-power-4), its the default size of Hashtable

If we notice here, hashmap with power-of-two length 16(2^4), only last four digits matter in the allocation of buckets, and these are the 4 binary lower bit digit variations that play prominent role in identifying the right bucket.

Keeping the above sequence in mind, we re-generated the hashcode from hash(int h) by passing the existing hascode which makes sure there is enough variation in the lower bits of the hashcode and then pass it to indexFor() method , this will ensure the lower bits of hashcode are used to identify the bucket and the rest higher bits are ignored.

For example, taking the same hascode sequences from above example

Our hash code 1111110111011010101101010111110 when regenerated with hash(int h) method, it generates new hashcode ==> 1111001111110011100110111011010

so here it is clear that because of the regenerated hashcode, the lower bits are will distributed/mixed leading to unique index which leads to different buckets avoiding collisions.

Why only these magic numbers 20, 12, 7 and 4. It is explained in the book: “The Art of Computer Programming” by Donald Knuth.

Here we are XORing the most significant bits of the number into the least significant bits (20, 12, 7, 4). The main purpose of this 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.

java.util.HashMap.java

Going back to previous steps:

1. re-generates the hashcode using hash(int h) method by passing user defined hashcode as an argument

2. generates index based on the re-generated hashcode and length of the data structure.

3. if key exists, it over-rides the element, else it will create a new entry in the hashmap at the index generated in STEP-2

Steps1&2 must be clear by now.

Step3:

What happens when two different keys have same hashcode?

1. if the keys are equal, i.e, to-be-inserted key and already-inserted key’s hashcodes are same and keys are same (via reference or via equals() method) then over-ride the previous key-value pair with the current key-value pair.

2. if keys are not equal, then store the key-value pair in the same bucket as that of the existing keys.

When collision happens in hashmap? it happens in case-2 of above question.

How do you retrieve value object when two keys with same hashcode are stored in hashmap? Using hashcode wo go to the right bucket and using equals we find the right element in the bucket and then return it.

How does different keys with same hashcode stored in hashmap? Usual answer is in bucket but technically they are all stored in a single linked list. Little difference is that insertion of new element to the linked list is made at the head instead of tail to avoid tail traversal.

What is bucket and what can be maximum number of buckets in hashmap? A bucket is an instance of the linked list (Entry Inner Class in my previous post) and we can have as many number of buckets as length of the hashmap at maximum, for example, in a hashmap of length 8, there can be maximum of 8 buckets, each is an instance of linked list.

java.util.HashMap.java

1. re-generates the hashcode using hash(int h) method by passing user defined hashcode as an argument

2. generates index based on the re-generated hashcode and length of the data structure.

3. point to the right bucket, i.e, table[i], and traverse through the linked list, which is constructed based on Entry inner class

4. when keys are equal and their hashcodes are equal then return the value mapped to that key

What is HashMap



"Have you used HashMap before" or  "What is HashMap? Why do we use it “

Almost everybody answers this with yes and then interviewee keep talking about common facts about HashMap like HashMap accept null while Hashtable doesn't, HashMap is not synchronized, HashMap is fast and so on along with basics like its stores key and value pairs etc. This shows that person has used HashMap  and quite familiar with the functionality HashMap offers but interview takes a sharp turn from here and next set of follow-up questions gets more detailed about fundamentals involved with HashMap in Java . Interviewer struck back with questions like
1) Why String, Integer and other wrapper classes are considered good keys ?
String, Integer and other wrapper classes are natural candidates of HashMap key, and String is most frequently used key as well because String is immutable and final,and overrides equals and hashcode() method. Other wrapper class also shares similar property. Immutabiility is required, in order to prevent changes on fields used to calculate hashCode() because if key object return different hashCode during insertion and retrieval than it won't be possible to get object from HashMap. Immutability is best as it offers other advantages as well like thread-safety, If you can  keep your hashCode same by only making certain fields final, then you go for that as well. Since equals() and hashCode() method is used during reterival of value object from HashMap, its important that key object correctly override these methodsand follow contact. If unequal object return different hashcode than chances of collision will be less which subsequently improve performance of HashMap.
Concept of hashing
Collision resolution in HashMap
Use of equals () and hashCode () and there importance in HashMap?
Benefit of immutable object?
Race condition on HashMap  in Java
Resizing of Java HashMap
How HashMap  works in JavaHashMap  works on principle of hashing, we have put() and get() method for storing and retrieving object form HashMap .When we pass an both key and value to put() method to store on HashMap , it uses key object hashcode() method to calculate hashcode and they by applying hashing on that hashcode it identifies bucket location for storing value object. While retrieving it uses key object equals method to find out correct key value pair and return value object associated with that key. HashMap  uses linked list in case of collision and object will be stored in next node of linked list.Also HashMap  stores both key+value tuple in every node of linked list.
"Do you Know how HashMap works in Java” or "How does get () method of HashMap works in Java"And then you get answers like I don't bother its standard Java API, you better look code on Java source or Open JDK; I can find it out in Google at any time etc. But some interviewee definitely answer this and will say "HashMap works on principle of hashing, we have put(key, value) and get(key) method for storing and retrieving Objects from HashMap. When we pass Key and Value object  to put() methodon Java HashMap, HashMap implementation calls hashCode method on Key object and applies returned hashcode into its own hashing function to find a bucket location for storing Entry object, important point to mention is that HashMap in Java stores both key and value object as Map.Entry in bucket which is essential to understand the retrieving logic. If people fails to recognize this and say it only stores Value in the bucket they will fail to explain the retrieving logic of any object stored in Java HashMap . This answer is very much acceptable and does make sense that interviewee has fair bit of knowledge on how hashing works and how HashMap  works in Java. But this is just start of story and confusion increases when you put interviewee on scenarios faced by Java developers on day by day basis. Next question could be about collision detection and collision resolution in Java HashMap  e.g.
"What will happen if two different objects have same hashcode?”Now from here onwards real confusion starts, Some time candidate will say that since hashcode is equal, both objects are equal and HashMap  will throw exception or not store them again etc, Then you might want to remind them about equals() and hashCode() contract  that two unequal object in Java can have same hashcode. Some will give up at this point and few will move ahead and say "Since hashcode is same, bucket location would be same and collision will occur in HashMap, Since HashMap use LinkedList to store object, this entry (object of Map.Entry comprise key and value )  will be stored in LinkedList. Great this answer make sense though there are many collision resolution methods available this is simplest and HashMap in Java does follow this. But story does not end here and interviewer asks
"How will you retrieve Value object  if two Keys will have same hashcode?”Interviewee will say we will call get() method and then HashMap uses Key Object's hashcode to find out bucket location and retrieves Value object but then you need to remind him that there are two Value objects are stored in same bucket , so they will say about traversal in LinkedList until we find the value object , then you ask how do you identify value object because you don't  have value object to compare ,Until they know that HashMap  stores both Key and Value in LinkedList node or as Map.Entry they won't be able to resolve this issue and will try and fail.
But those bunch of people who remember this key information will say that after finding bucket location , we will call keys.equals() method to identify correct node in LinkedList and return associated value object for that key in Java HashMap . Perfect this is the correct answer.
In many cases interviewee fails at this stage because they get confused between hashCode() and equals() or keys and values object in Java HashMap  which is pretty obvious because they are dealing with the hashcode() in all previous questions and equals() come in picture only in case of retrieving value object from HashMap in Java. Some good developer point out here that using immutable, final object with proper equals() and hashcode() implementation would act as perfect Java HashMap  keys and improve performance of Java HashMap  by reducing collision. Immutability also allows caching there hashcode of different keys which makes overall retrieval process very fast and suggest that String and various wrapper classes e.g. Integer very good keys in Java HashMap.
Now if you clear this entire Java HashMap interview,
 You will be surprised by this very interesting question "What happens On HashMap in Java if the size of the HashMap  exceeds a given threshold defined by load factor ?". Until you know how HashMap  works exactly you won't be able to answer this question. If the size of the Map exceeds a given threshold defined by load-factor e.g. if load factor is .75 it will act to re-size the map once it filled 75%. Similar to other collection classes like ArrayList,  Java HashMap re-size itself by creating a new bucket array of size twice of previous size of HashMap , and then start putting every old element into that new bucket array. This process is called rehashing because it also applies hash function to find new bucket location.
If you manage to answer this question on HashMap in Java you will be greeted by "do you see any problem with resizing of HashMap  in Java" , you might not be able to pick the context and then he will try to give you hint about multiple thread accessing the Java HashMap and potentially looking for race condition on HashMap  in Java.
So the answer is Yes there is potential race condition exists while resizing HashMap in Java, if two thread at the same time found that now HashMap needs resizing and they both try to resizing. on the process of resizing of HashMap in Java , the element in bucket which is stored in linked list get reversed in order during there migration to new bucket because java HashMap  doesn't append the new element at tail instead it append new element at head to avoid tail traversing. If race condition happens then you will end up with an infinite loop. Though this point you can potentially argue that what the hell makes you think to use HashMap  in multi-threaded environment to interviewer :)

Few more question on HashMap in Java which is contributed by readers of Javarevisited blog  :


2) Can we use any custom object as key in HashMap ?This is an extension of previous questions. Ofcourse you can use any Object as key in Java HashMap provided it follows equals and hashCode contract and its hashCode should not vary once the object is inserted into Map. If custom object is Immutable than this will be already taken care because you can not change it once created.
3) Can we use ConcurrentHashMap in place of Hashtable ?This is another question which getting popular due to increasing popularity of ConcurrentHashMap. Since we know Hashtable is synchronized but ConcurrentHashMap provides better concurrency by only locking portion of map determined by concurrency level. ConcurrentHashMap is certainly introduced as Hashtable and can be used in place of it but Hashtable provide stronger thread-safety than ConcurrentHashMap. See my post difference between Hashtable and ConcurrentHashMap for more details.
Personally, I like this question because of its depth and number of concept it touches indirectly, if you look at questions asked during interview this HashMap  questions has verified

Just to summarize here are the answers which does makes sense for above questions