WeakHashMap in Java

What is WeakHashMap

WeakHashMap is based on Hash table implementation of the Map interface but with weak keys.

An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use.

When a key has been discarded due to weakness characteristics of keys its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.

Like HashMap both null values and the null key are supported.
WeakHashMap has performance characteristics similar to the HashMap and has the same efficiency parameters of initial capacity and load factor.

Each key object in a WeakHashMap is stored indirectly as the referent of a weak reference. Therefore a key will automatically be removed only after the weak references to it, both inside and outside of the map, have been cleared by the garbage collector.
Unlike HashMap, WeakHashMap does not implement Serializable and Cloneable interface.

An Example

A good example of WeakHashMap in action is the ThreadLocal class. ThreadLocal uses a WeakHashMap internally where the keys are Threads. When you call get(), the ThreadLocal class gets a reference to the current thread and looks up its value in the WeakHashMap. When a thread dies, it becomes unreachable and the WeakHashMap automatically removes its mapping. If ThreadLocal used a regular HashMap, then the Threads stored in it would never become unreachable and they could hang around for a long, long time. This would essentially be a memory leak.

Accessing in multi-threaded environment

Like HashMap, LinkedHashMap and TreeMap, WeakHashMap implementation is not synchronized. So multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be “wrapped” using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:

Map m = Collections.synchronizedMap(new WeakHashMap(...));

WeakHashMap Fail-fast

If the map is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException.  Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

The fail-fast behavior of an iterator cannot be guaranteed and iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Set keys = weakHashMap.keySet();
for (Object key : keys) {
    weakHashMap.put(someObject, someValue); //it will throw the ConcurrentModificationException here
}

or

Set keys = weakHashMap.keySet();
for (Object key : keys) {
    weakHashMap.remove(key); //it will throw the ConcurrentModificationException here
}

Example

package com.roytuts.collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
public class WeakHashMapExample {
    public static void main(String[] args) {
        Map<String, String> wMap = new WeakHashMap<>();
        wMap.put("RedHat", "Unix");
        wMap.put("Google", "Android");
        wMap.put("Apple", "iOS");
        wMap.put("Microsoft", "Windows");
        // iterate over WeakHashMap
        wMap.forEach((k, v) -> System.out.println(k + " => " + v));
        System.out.println();
        // iterate using iterator
        Iterator<Entry<String, String>> iterator = wMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<java.lang.String, java.lang.String> entry = (Map.Entry<java.lang.String, java.lang.String>) iterator
                    .next();
            System.out.println(entry.getKey() + " => " + entry.getValue());
        }
        /*Set keys = wMap.keySet();
        for (Object key : keys) {
            wMap.put("abc", "value"); // it will throw the
                                        // ConcurrentModificationException here
        }*/
        System.out.println();
        System.out.println("Before garbage collected : " + wMap);
        wMap = null;
        // System.gc();
        System.out.println("After garbage collected : " + wMap);
    }
}

Output

Apple => iOS
RedHat => Unix
Microsoft => Windows
Google => Android
Google => Android
Microsoft => Windows
Apple => iOS
RedHat => Unix
Before garbage collected : {Google=Android, Microsoft=Windows, Apple=iOS, RedHat=Unix}
After garbage collected : null

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *