Java WeakHashMap


The WeakHashMap class in Java, part of the java.util package, is a unique implementation of the Map interface that uses weak references for its keys. This feature makes WeakHashMap particularly useful for memory-sensitive applications, where keys are automatically removed from the map once they are no longer in use elsewhere in the application (i.e., when they are garbage-collected).


What is a WeakHashMap in Java?

A WeakHashMap is a Map implementation where the keys are stored as weak references. A weak reference allows the garbage collector to reclaim the key objects as soon as they are no longer in use, even if they are still present in the map. This behavior helps prevent memory leaks, making WeakHashMap particularly useful in scenarios where you want to ensure that objects are automatically removed from the map when they are no longer needed.

Key features of WeakHashMap:

  • Weak References for Keys: The keys in a WeakHashMap are weakly referenced, meaning they can be garbage-collected when no strong references to them exist.
  • Values: The values in a WeakHashMap are strongly referenced (normal references).
  • Automatic Cleanup: When a key object is no longer reachable from anywhere else in the application, it will be removed from the WeakHashMap.
  • Thread-Safety: WeakHashMap is not synchronized, so it is not thread-safe by default. If you need thread-safety, you can use a ConcurrentHashMap or synchronize it manually.

Constructor of WeakHashMap

The WeakHashMap class provides a few constructors for different use cases:

WeakHashMap()                                  // Creates an empty map with the default initial capacity (16) and load factor (0.75).
WeakHashMap(int initialCapacity)               // Creates a map with the specified initial capacity.
WeakHashMap(int initialCapacity, float loadFactor)  // Creates a map with the specified initial capacity and load factor.
WeakHashMap(Map<? extends K, ? extends V> m)   // Creates a map with the same mappings as the specified map.
  • initialCapacity: The initial capacity of the map, which determines the number of buckets.
  • loadFactor: The load factor, which determines when the map will resize.
  • Map<? extends K, ? extends V> m: Initializes the map with the same key-value pairs as the provided map.

Key Methods of WeakHashMap

Some commonly used methods of the WeakHashMap class are similar to those in other Map implementations:

  • put(K key, V value): Adds a key-value pair to the map. If the key already exists, it updates the value.

    map.put("apple", 1);
    
  • get(Object key): Retrieves the value associated with the specified key.

    Integer value = map.get("apple");  // Returns the value associated with 'apple'
    
  • containsKey(Object key): Checks if the map contains the specified key.

    boolean exists = map.containsKey("apple");  // Returns true if "apple" is a key in the map
    
  • remove(Object key): Removes the key-value pair associated with the specified key.

    map.remove("apple");  // Removes the key-value pair for 'apple'
    
  • size(): Returns the number of key-value pairs in the map.

    int size = map.size();  // Returns the number of entries in the map
    
  • keySet(): Returns a set view of all the keys in the map.

    Set<String> keys = map.keySet();  // Returns a Set of all the keys
    
  • clear(): Removes all key-value pairs from the map.

    map.clear();  // Clears the map
    

Example 1: Basic Usage of WeakHashMap

Here's an example that demonstrates how a WeakHashMap works:

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

public class WeakHashMapExample {
    public static void main(String[] args) {
        // Create a WeakHashMap
        Map<String, Integer> map = new WeakHashMap<>();

        // Add some key-value pairs to the map
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);

        // Print the map size
        System.out.println("Size before GC: " + map.size());  // Output: 3

        // Make the key "banana" eligible for garbage collection
        String keyToGC = "banana";
        keyToGC = null;

        // Suggest garbage collection
        System.gc();

        // Print the map size after garbage collection
        System.out.println("Size after GC: " + map.size());  // Output: 2 (if "banana" is garbage collected)
    }
}

Output:

Size before GC: 3
Size after GC: 2

In this example:

  • We create a WeakHashMap and add some key-value pairs.
  • After setting the key "banana" to null, the key becomes eligible for garbage collection.
  • After calling System.gc(), the garbage collector may remove the entry with the "banana" key from the map, reducing the map size.

Example 2: WeakHashMap with Custom Objects

In this example, we use custom objects as keys in a WeakHashMap:

import java.util.WeakHashMap;

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    protected void finalize() {
        System.out.println(name + " is being garbage collected");
    }
}

public class WeakHashMapCustomObjectExample {
    public static void main(String[] args) {
        // Create a WeakHashMap with Person objects as keys
        WeakHashMap<Person, String> map = new WeakHashMap<>();

        // Create some Person objects and add them to the map
        Person person1 = new Person("John");
        Person person2 = new Person("Alice");

        map.put(person1, "Engineer");
        map.put(person2, "Doctor");

        // Print map size
        System.out.println("Map size before GC: " + map.size());  // Output: 2

        // Remove strong references to person1
        person1 = null;

        // Suggest garbage collection
        System.gc();

        // Print map size after GC
        System.out.println("Map size after GC: " + map.size());  // Output: 1 (if person1 is garbage collected)
    }
}

Output:

Map size before GC: 2
John is being garbage collected
Map size after GC: 1

In this example:

  • We use Person objects as keys in a WeakHashMap.
  • After the reference to person1 is set to null, it becomes eligible for garbage collection.
  • When System.gc() is called, person1 is garbage collected, and the map is updated accordingly.

Performance Considerations

  • Time Complexity: Operations like put(), get(), and remove() in WeakHashMap are generally O(1) on average. However, the process of checking for garbage-collected keys might add a slight overhead compared to other Map implementations.
  • Garbage Collection Impact: The behavior of WeakHashMap is directly tied to garbage collection. If keys are not reachable, they will be removed from the map automatically. This makes WeakHashMap useful for managing memory but also means that the size of the map can fluctuate unpredictably as the garbage collector runs.

When to Use WeakHashMap

You should use WeakHashMap in scenarios where:

  1. You want to store objects with keys that should be garbage-collected when they are no longer in use.
  2. You need a map that helps avoid memory leaks by automatically removing unused keys.
  3. You are implementing caching mechanisms where cached objects should be removed once they are no longer referenced elsewhere in the application.