10 minutes read

The Java Collection Framework provides different implementations of the Map interface. Each of them has its own features that we should be aware of. This topic will help you explore WeakHashMap and its behavior. You will learn how it is different from a regular HashMap and in which cases to use it.

WeakHashMap: the basics

You already know that there are different types of references and the garbage collector has a different removal approach for each of them. WeakHashMap is implemented using weak references, which means an object can be recycled at the first garbage collection if there is only a weak reference to it. In this case, the object is the map key which is indirectly connected to the map via a reference object, and the key itself is a referent.
Now, let's create a Person class, which will be used as a WeakHashMap key:

class Person {
    private String name;
    private int birthYear;

    public Person(String name, int birthYear) {
        this.name = name;
        this.birthYear = birthYear;
    }

    // getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return birthYear == person.birthYear && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, birthYear);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", birthYear=" + birthYear +
                '}';
    }
}

We'll add just one key-value pair to understand the WeakHashMap structure:

public class WeakHashMapDemo {
    public static void main(String[] args) {
        Person joshua = new Person("Joshua Bloch", 1961);

        Map<Person, String> map = new WeakHashMap();
        map.put(joshua, "Effective Java"); 
    }
}

The diagram below shows an example of the relationship between an entry and a key object from our code sample.

In OpenJDK, a WeakHashMap entry is represented by the nested Entry<K,V> static class. It extends the WeakReference<Object> class and implements a Map.Entry<K,V> interface. All the further material in this topic is based on OpenJDK.

Stack and Heap

Here, an entry is stored in the map as long as the joshua variable references the key object. Once this strong reference is removed, only a weak reference to the object remains, so the GC can remove it. And when the key is recycled, the entry will also be recycled. This is the main difference between WeakHashMap and HashMap. If we used a HashMap, removing the reference of the joshua variable from the object would not lead to removing the key, since the HashMap also refers to it by a strong reference.

WeakHashMap vs HashMap

Now that you've learned the key feature of WeakHashMap, it's time to see it in action. We will write the same code using WeakHashMap and HashMap to compare their behavior. To do this, we will modify the code from the previous section to get a small application where we will add key-value pairs and then remove a strong reference from one key. You can copy the code and test it yourself.

If you are not familiar with threads yet, look at line 12: there we suspend the thread in which the application is executed for 250 milliseconds. This is done in order to give the JVM time to do its job: remove the key and the whole entry.

public class WeakHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        Person joshua = new Person("Joshua Bloch", 1961);
        Person bruce = new Person("Bruce Eckel", 1957);

        Map<Person, String> map = new WeakHashMap();
        map.put(joshua, "Effective Java");
        map.put(bruce, "Thinking in Java"); 
        
        joshua = null;
        System.gc();
        Thread.sleep(250);

        System.out.println(map.size()); // 1
    }
}

On line 10 we remove the strong reference by which the joshua variable referred to the object, thereby leaving only a weak reference from the map. On the next line, we call the GC to perform garbage collection. In that case, the object will be recycled and it will lead to the removal of the entry as a result.

Important note: calling the System.gc() doesn't guarantee that the GC will perform garbage collection. Don't be confused if the WeakHashMap size remains 2 on your machine. In this topic, we assume that calling this method led to garbage collection.

Now let's run the same code using a HashMap to see what result we will get:

public class WeakHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        Person joshua = new Person("Joshua Bloch", 1961);
        Person bruce = new Person("Bruce Eckel", 1957);

        Map<Person, String> map = new HashMap();
        map.put(joshua, "Effective Java");
        map.put(bruce, "Thinking in Java"); 
        
        joshua = null;
        System.gc();
        Thread.sleep(250);

        System.out.println(map.size()); // 2
    }
}

Remember what we said about HashMap in the previous section? After removing the joshua variable reference from the key object, there is still a strong reference from the HashMap, so the object is not recycled.

WeakHashMap as a cache

In many resources, you can find information that WeakHashMap is used to store the cache data. It is important to understand that this does not mean a cache as we know it. The purpose of the regular cache is to store data that is frequently used, and to provide quick access to it. Social networks and messengers are good examples. They cache data, especially profile photos, on the device, so as not to download them from the internet. Thanks to this, we save internet traffic, and using data from the device is much faster than downloading it from a server that could be located in another country. The WeakHashMap-based cache, on the other hand, has a different purpose: it stores an entry while its key is in use. In other words, there is at least one strong reference to it. Such a cache is more effective from the memory usage perspective, and it will be especially useful for storing large objects.

Conclusion

In this topic, we introduced you to a Map implementation that uses weak references for its keys. To sum up, when working with WeakHashMap, you should keep in mind that entries can be automatically deleted. Its state will change even if you don't perform any update operation, which is impossible when working with a regular HashMap. If that is exactly the behavior you expect to get, this map implementation will be a great choice to optimize your application.

17 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo