Friday, 13 June 2025

HashMap vs SynchronizedMap vs ConcurrentHashMap in Java

 

🔍 1)What is a HashMap?

A HashMap is a part of the Java Collection Framework that stores data in key-value pairs using a hash table.

📦 Package: java.util.HashMap
🔑 Keys must be unique
❗ Allows one null key and multiple null values


🧠 How Does It Work?

  • Internally uses an array of buckets (array of Node<K,V>).

  • Each key’s hashCode() is used to find a bucket index.

  • If two keys have the same hash, a linked list or balanced tree (Java 8+) is used in that bucket (called collision handling).


📌 Key Characteristics

FeatureDescription
Thread-safe❌ Not thread-safe
Allows null key✅ Only one null key
Allows null values✅ Any number of null values
Order maintained❌ No (use LinkedHashMap if needed)
Performance⚡ Fast O(1) for get/put in average case
Load Factor (default)0.75 (when to resize)
Initial Capacity (default)16

✅ Example – Basic Usage

import java.util.HashMap;
public class Main { public static void main(String[] args) { HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Apple"); map.put(2, "Banana"); map.put(null, "NullKey"); map.put(3, null); // null value allowed System.out.println(map.get(1)); // Apple System.out.println(map.get(null)); // NullKey System.out.println(map); // {null=NullKey, 1=Apple, 2=Banana, 3=null} } }

🔧 Common Methods

MethodDescription
put(K, V)Add/update a key-value pair
get(K)Get value by key
remove(K)Remove key
containsKey(K)Check if key exists
containsValue(V)Check if value exists
keySet()Returns set of keys
values()Returns collection of values
entrySet()Returns set of key-value pairs

🔁 Iteration Examples

🔹 Keys

for (Integer key : map.keySet()) {
System.out.println("Key: " + key); }

🔹 Values

for (String value : map.values()) {
System.out.println("Value: " + value); }

🔹 Entry (key-value pair)

for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue()); }

🚫 Not Thread-Safe

In multi-threaded environments, use:

  • Collections.synchronizedMap(new HashMap<>())

  • OR prefer ConcurrentHashMap for better concurrency


⚙️ Internal Working (Simplified)

1. Key is passed to hashCode()
2. Hash value is computed and converted to an array index 3. Value is stored at that index 4. If collision occurs → linked list / tree is used at that index 5. Resize happens if size > capacity * load factor

🆚 HashMap vs Other Maps

FeatureHashMapLinkedHashMapTreeMapHashtable
Ordering✅ Insertion✅ Sorted keys
Null Keys✅ 1✅ 1❌ (throws NPE)❌ (throws NPE)
Thread-safe✅ (but obsolete)
Performance✅ Fast✅ Slightly slower⚠️ Slower⚠️ Slower

✅ When to Use HashMap

Use HashMap when:

  • You need fast key-value lookups

  • You don’t need order

  • You’re working in a single-threaded environment

  • You’re okay with allowing null key/value


❗ Avoid When

  • You need sorted data → use TreeMap

  • You need insertion order → use LinkedHashMap

  • You need thread safety → use ConcurrentHashMap or Collections.synchronizedMap()

🔍 2)What is a Synchronized Map?

A SynchronizedMap is a wrapper around a regular map (like HashMap) that makes all its method calls thread-safe using synchronization (i.e., internal synchronized blocks).

🔒 It is useful when you want a thread-safe version of HashMap but don’t need the performance of ConcurrentHashMap.


📦 How to Create a SynchronizedMap

Use Collections.synchronizedMap():

import java.util.*;
public class Example { public static void main(String[] args) { Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); map.put("1", "One"); map.put("2", "Two"); System.out.println(map.get("1")); // One } }

🧠 How It Works Internally

Collections.synchronizedMap() returns a wrapper object where every method is synchronized on the map’s monitor (synchronized (map)):

public V put(K key, V value) {
synchronized (mutex) { return m.put(key, value); } }

This ensures only one thread can access any method at a time — even for read operations.


⚠️ Iteration Must Be Manually Synchronized

Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
synchronized (map) { for (String key : map.keySet()) { System.out.println(key + " => " + map.get(key)); } }

❗ Failing to synchronize during iteration may cause ConcurrentModificationException.

✅ When to Use SynchronizedMap

Use SynchronizedMap when:

  • You have low concurrency.

  • You want backward compatibility with old HashMap code.

  • You want nulls to be allowed, unlike ConcurrentHashMap.

------------------------------------------------------------------------------------------------

🔍 3)What is ConcurrentHashMap?

ConcurrentHashMap is a thread-safe, high-performance implementation of a hash map in Java, designed for concurrent access without the need for external synchronization.

📦 Package: java.util.concurrent.ConcurrentHashMap
✅ Introduced in Java 5

✅ Why Use It?

  • We need safe access by multiple threads to a shared map.

  • We want better performance than synchronizing an entire HashMap or using Hashtable.

  • And it provides atomic methods like putIfAbsent(), compute(), merge() so, we better to use 

    ConcurrentHashMap

🚫 Why not use HashMap in multi-threading?

HashMap is not thread-safe. Using it in multi-threaded code can lead to:

  • ConcurrentModificationException

  • Corrupted data

  • Infinite loops (e.g., during rehashing)

⚙️ How ConcurrentHashMap Works

Java VersionInternal Design
Java 7Segment-based locking
Java 8+ Bucket-level fine-grained locks (no segments), uses CAS (Compare-And-Swap)

Instead of locking the entire map, only portions (buckets) of the map are locked during updates — greatly improving concurrency.

🧑‍💻 Example – Basic Usage

import java.util.concurrent.ConcurrentHashMap;
public class Example { public static void main(String[] args) { ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(); map.put("1", "One"); map.put("2", "Two"); System.out.println(map.get("1")); // One map.putIfAbsent("2", "TwoAgain"); // Won't overwrite map.compute("2", (k, v) -> v + "-Updated"); System.out.println(map); // {1=One, 2=Two-Updated} } }

❌ Null Values Are Not Allowed

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put(null, "value"); // ❌ NullPointerException map.put("key", null); // ❌ NullPointerException

Reason: null is ambiguous in concurrent environments (e.g., get returning null — is it absent or null?).


🔄 Atomic Methods

MethodPurpose
putIfAbsent(key, val)Insert only if key is missing
compute(key, remapFn)Update value atomically with a remapping function
computeIfAbsent()       Lazily compute if key is missing
computeIfPresent()Update only if key is present
merge()Merge existing and new values
forEach() Safe concurrent iteration

🔧 Multithreaded Example

import java.util.concurrent.*;
public class ConcurrentExample { public static void main(String[] args) throws InterruptedException { ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); ExecutorService executor = Executors.newFixedThreadPool(5); Runnable task = () -> { for (int i = 0; i < 1000; i++) { map.merge("count", 1, Integer::sum); } }; for (int i = 0; i < 5; i++) executor.submit(task); executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); System.out.println("Final count: " + map.get("count")); // 5000 } }

🔐 ConcurrentHashMap vs SynchronizedMap vs Hashtable

Feature HashMap Hashtable SynchronizedMap ConcurrentHashMap
Thread-safe ❌ No ✅ Yes ✅ Yes (via wrapper) ✅ Yes (better concurrency)
Performance (multi-thread) 🚫 Bad ⚠️ Slow ⚠️ Slower ✅ Fast, scalable
Null keys/values ✅ Allowed ❌ Disallowed ✅ Allowed ❌ Not allowed
Read/Write Locking ❌ None Full sync (method) Full sync (method) Fine-grained (bucket-level)
Use in modern apps ❌ No ❌ Obsolete ⚠️ Legacy-compatible ✅ Preferred

✅ Summary

  • Use ConcurrentHashMap when you need a thread-safe map with high concurrency.

  • Nulls are not allowed.

  • Provides atomic operations like putIfAbsent(), compute(), and merge().

  • Faster and more scalable than legacy alternatives like Hashtable.


 

No comments:

Post a Comment