Java - you need to know the difference between HashMap, Hashtable and concurrent HashMap

Posted by csaba on Mon, 31 Jan 2022 15:23:42 +0100

Write in front

According to others, during the interview, the interviewer often asks a question: what is the difference between HashMap, Hashtable and concurrent HashMap?

In short:

  • HashMap is thread unsafe. When multi-threaded operation occurs, there will be potential safety hazards.
  • HashTable is thread safe, in which all methods are added with the synchronized keyword, that is, HashTable uses the method lock to lock the whole put method, which leads to low efficiency. If the put method is compared to a yard with many rooms, the lock of HathTable is equivalent to locking the gate of the yard, so you can't enter the yard.
  • ConcurrentHashMap is also thread safe, but it uses block lock (the synchronized keyword modifies not the whole method body, but part of the code block). Here, the put method is still compared to a yard with many rooms, so it is equivalent to locking the rooms with potential safety hazards in the yard. In this way, other rooms can still enter, but you can't enter the rooms with potential safety hazards for the time being.

Next, let me explain it with three simple code examples.

HashMap

HashMap is thread unsafe. The put method is not locked in the source code. When facing multithreading, there will be thread safety problems.

The following is a simple example to demonstrate. Create three threads and start them. In the run method, save 100 values to the map through the for loop, and then output the size of the map. Normally, the size of the map should be 100, while 190 is output here.

import java.util.HashMap;
import java.util.Map;

/**
 *
 */
public class HashMapTest implements Runnable {

    static Map<String,String> map=new HashMap<>();

    @Override
    public void run() {
        long startTime=System.currentTimeMillis();
        for (int i=0;i<100;i++) {
            map.put(i + "","value");
        }
        long endTime=System.currentTimeMillis();
        System.out.println((endTime-startTime) + "ms");
    }

    public static void main(String[] args) {
        Thread thread1=new Thread(new HashMapTest());
        Thread thread2=new Thread(new HashMapTest());
        Thread thread3=new Thread(new HashMapTest());

        thread1.start();
        thread2.start();
        thread3.start();

        Thread currentThread=Thread.currentThread();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(map.size());
    }
}

Hashtable

HashTable uses a lock, which is directly added to the put method. The thread must be safe. Here, while testing thread safety, let's take a look at the execution time.

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 *
 */
public class HashtableTest implements Runnable {

    static Map<String,String> map=new Hashtable<>();

    @Override
    public void run() {
        long startTime=System.currentTimeMillis();
        for (int i=0;i<100;i++) {
            map.put(i + "","value");
        }
        long endTime=System.currentTimeMillis();
        System.out.println((endTime-startTime) + "ms");
    }

    public static void main(String[] args) {
        Thread thread1=new Thread(new HashtableTest());
        Thread thread2=new Thread(new HashtableTest());
        Thread thread3=new Thread(new HashtableTest());

        thread1.start();
        thread2.start();
        thread3.start();

        Thread currentThread=Thread.currentThread();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(map.size());
    }
}

It can be seen here that after repeatedly executing this code, the final size of the map set is always 100, no more, no less, just right, and the storage time is about 12ms.

ConcurrentHashMap

ConcurrentHashMap uses block lock, which is not safe. It can't be locked without lock. It can't be fully locked, so I'll lock it! See whether the lock of this block is fast or slow for the Hashtable method lock.  

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public class ConcurrentHashMapTest implements Runnable {

    static Map<String,String> map=new ConcurrentHashMap<>();

    @Override
    public void run() {
        long startTime=System.currentTimeMillis();
        for (int i=0;i<100;i++) {
            map.put(i + "","value");
        }
        long endTime=System.currentTimeMillis();
        System.out.println((endTime-startTime) + "ms");
    }

    public static void main(String[] args) {
        Thread thread1=new Thread(new ConcurrentHashMapTest());
        Thread thread2=new Thread(new ConcurrentHashMapTest());
        Thread thread3=new Thread(new ConcurrentHashMapTest());

        thread1.start();
        thread2.start();
        thread3.start();

        Thread currentThread=Thread.currentThread();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(map.size());
    }
}

It can be seen that the size of the map set finally obtained here is always 100, and the storage time is shorter than that of Hashtable, which is basically about 10ms.