Monday, July 15, 2024

What is WeakHashMap in Java? When to use it? Example Tutorial

WeakHashMap is an implementation of Map interface in Java. It's a special Map where entries can be reclaimed by Garbage collector if there is no more strong reference exists to the value, apart from the key in the WeakHashMap, which is anyway the weak reference. This property allows a WeakHashMap to be used as Cache, where Garbage Collector can automatically cleanup dead entries. I mean those objects which are only alive inside map but there is no outside entries. Worth noting is that the key are stored as WeakReferences inside WeakHashMap which allows values to be reclaimed if there is no strong reference exists outside.
Keys in WeakHashMap are also subject to garbage collection because they are stored as WeakReference and when a key is garbage collected, its corresponding entry in the WeakHashMap is automatically removed. 

This happens during the next access attempt to the map, either by calling get(), put() or containsKey() method or during iteration over WeakHashMap. 

Because of this special behavior, WeakHashMap is an interesting topic and a special class and its also often asked in Java interviews. I have also shared WeakHashMAp related question earlier on 133 Java Interview Question list and 40+ HashMap related questions article. 


What is Weak HashMap in Java?

As explained in the fist paragraph, its a special type of Map in Java where keys are stored as WeakReference so that they can be reclaimed by Garbage Collector if there is no strong reference to keys or values exists outside the Map.  

In other words, they are Garbage Collection friendly Map which can be used to stored association of temporary objects. You can use them as Cache where entries can automatically be removed when there are no longer reference in the application.

The removal of entries with garbage collected keys are both thread-safe and automatic. 

Here is a nice diagram which shows what is WeakHashMap and how it works:




When to use WeakHashMap in Java Application?

As I said its a special type of Map and there are used on special situations like when you need a auto-removal Cache where keys can be removed if they are no longer referenced in application.

You can also use WeakHashMap for canonicalizing mappings, where you want to ensure that a particular value is always represented by the same object, but you don't want to keep those objects around if they're no longer needed.

Though its worth noting that you should only use WeakHashMap when you specifically need weak references for keys. If you need strong references, use other Map implementations like HashMap or TreeMap or LinkedHashMap depending upon your requirement. 

Here is a nice diagram which shows how you can use WeakHashMap as a memory sensitive Cache




Java WeakHashMap Example

Here is a simple example of WeakHashMap in Java. In this example, I have created a Cache of books called bestSellers using WeakHashMap class. This Map stores a book and number of copies it sold. You can see when GC triggered the books which are not reference in the program are removed from WeakHashMap. 

package beginner;

import java.util.Map;
import java.util.WeakHashMap;

/**
 * Simple Java program to demonstrate working of WeakHashMap in Java
 * 
 * @author WINDOWS 8
 *
 */
public class HelloWorldApp {

    public static void main(String... args){
       
        Map<Book, Integer> bestSellers = new WeakHashMap<Book, Integer>();
        
        bestSellers.put(new Book(1, "Effective Java", "Joshua Bloch"), 
100000);
        bestSellers.put( new Book(2, "Head First Java", "Kathy Sierra"),
 200000);
        bestSellers.put(new Book(3, "Thinking in Java", "Bruce Eickl"), 
100000);
        bestSellers.put(new Book(4, "Java Concurrency", "Brian Goetz"), 
100000);
        
        System.out.println("Number of entries into Map before GC: "
        + bestSellers.size() );
        
        // this is triggering GC on my machine, it may not on your machine but if you keep
        // adding Books, it will certainly trigger GC at some point
        bestSellers.put(new Book(5, "Java Threads", "Unknown"), 100000);

        System.out.println("Number of entries into Map after GC: " 
         + bestSellers.size() );
        
        // adding a Strong reference
        Book javaPuzzlers = new Book(7, "Java Puzzlers", "Josh Bloch");
        bestSellers.put(javaPuzzlers, 200000);
        System.out.println("Number of entries into Map after GC: " 
      + bestSellers.size() );
        System.out.println("Map: " + bestSellers );
    }
}

class Book {
    private int id;
    private String title;
    private String author;
    private int[] memoryHogger = new int[1000000];
    public Book(int id, String title, String author) {
        super();
        this.id = id;
        this.title = title;
        this.author = author;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((author == null) ? 0 : author.hashCode());
        result = prime * result + id;
        result = prime * result + ((title == null) ? 0 : title.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Book)) {
            return false;
        }
        Book other = (Book) obj;
        if (author == null) {
            if (other.author != null) {
                return false;
            }
        } else if (!author.equals(other.author)) {
            return false;
        }
        if (id != other.id) {
            return false;
        }
        if (title == null) {
            if (other.title != null) {
                return false;
            }
        } else if (!title.equals(other.title)) {
            return false;
        }
        return true;
    }
    
    @Override
    public String toString() {
        return title;
    }
}
Output
Number of entries into Map before GC: 4
Number of entries into Map after GC: 1
Number of entries into Map after GC: 1
Map: {Java Puzzlers=200000}

You can see that number of entries into WeakHashMap is reduced from 4 to 1 after garbage collection and all the books which are not referenced elsewhere are removed. 

Important points about WeakHashMap in Java

Now, let's revise important characteristics of WeakHashMap in Java. These are the properties you must remember to effectively use WeakHashMap class in Java.

1) Entries in HashMap can be reclaimed by garbage collector if there are no other strong references to the value, this makes them useful for caches.

2) Keys inserted into WeakHashMap are automatically wrapped in java.lang.ref.WeakReference

3) WeakhashMap is useful if the desired lifetime of cache entries are determined by external references to the key, not the value.


That's all about WeakHashMap in Java. As I said, its s special implementation of Map interface in Java where keys are wrapped as WeakReferene so that Garbage collector can remove them if they are not referenced outside. Removal of the key  by GC also removes associated entries and its done in a thread-safe manner. WeakHashMap is useful for creating cache of temporary objects which come and go. 

1 comment :

Anonymous said...

I have never used this class in my 10 years of Java career but good to know about it. Thanks

Post a Comment