WeakHashMap in Java – Guide with Examples
WeakHashMap is a Map whose keys are held by weak references. When a key is no longer used anywhere else in your program, the garbage collector may delete it, and its entry quietly disappears from the map.
That makes WeakHashMap useful for attaching extra data to objects without stopping them from being garbage-collected, a common cause of memory leaks with ordinary maps. It lives in java.util and has been available since JDK 1.2.
1. What Is a WeakHashMap?
In a normal HashMap, the map holds a strong reference to each key. As long as the map exists, its keys can never be garbage-collected, even if nothing else uses them. A WeakHashMap holds keys weakly: the map alone does not keep a key alive.
Think of a coat check that throws away a coat once its owner has left the building. The ticket (the entry) is only useful while the owner (the key) is still around.
- Weak keys, strong values.
- Entries are removed automatically after their key is garbage-collected.
- Otherwise behaves like HashMap: O(1) operations, one
nullkey,nullvalues, no order. - Not synchronized.
size()and iteration results can change between calls even if your code does not modify the map.
2. Class Hierarchy and Declaration
public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>
Unlike HashMap, it does not implement Cloneable or Serializable.
3. Strong, Soft, Weak and Phantom References
Java has four strengths of reference, from java.lang.ref:
| Reference | When the object can be collected | Typical use |
|---|---|---|
| Strong (normal variable) | Never, while the reference exists | Everyday code |
SoftReference | Only when memory is running low | Memory-sensitive caches |
WeakReference | At the next GC, if no strong references remain | WeakHashMap keys, listeners |
PhantomReference | Already collected; used for clean-up notification | Resource clean-up (Cleaner) |
4. WeakHashMap vs HashMap in Action
Both maps receive one session object. After the program drops its own references and the garbage collector runs, only the WeakHashMap lets go:
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class WeakVsHashMap {
static class Session {
final String user;
Session(String user) { this.user = user; }
}
// Ask the JVM to collect garbage until the map shrinks (or we give up)
static void waitForGc(Map<?, ?> map, int expectedSize) throws InterruptedException {
for (int i = 0; i < 50 && map.size() != expectedSize; i++) {
System.gc();
Thread.sleep(20);
}
}
public static void main(String[] args) throws InterruptedException {
Map<Session, String> strong = new HashMap<>();
Map<Session, String> weak = new WeakHashMap<>();
Session s1 = new Session("asha");
Session s2 = new Session("ravi");
strong.put(s1, "cart: 3 items");
weak.put(s2, "cart: 1 item");
System.out.println("Before: HashMap size=" + strong.size() + ", WeakHashMap size=" + weak.size());
s1 = null; // no more references from our code...
s2 = null;
waitForGc(weak, 0);
System.out.println("After GC: HashMap size=" + strong.size() + ", WeakHashMap size=" + weak.size());
}
}
Before: HashMap size=1, WeakHashMap size=1 After GC: HashMap size=1, WeakHashMap size=0
System.gc() is only a request, so the example asks a few times. Real programs never need to call it; the entries disappear whenever the JVM decides to collect garbage.
5. How WeakHashMap Works Internally
- Each entry is a subclass of
WeakReferencepointing to the key, plus a strong field for the value. - Every entry is registered with a
ReferenceQueue. - When the GC collects a key, it puts the entry's weak reference into that queue.
- On the next call to almost any map method (
get,put,size…), the map drains the queue (expungeStaleEntries()) and removes those entries.
So entries are removed lazily, during later map operations, not at the exact moment of garbage collection.
6. Common Pitfalls
String literals and cached values as keys
String literals are stored in the string pool and stay reachable for the life of the class, so they are never collected. Small Integer values (−128 to 127) and enum constants are cached too.
import java.util.Map;
import java.util.WeakHashMap;
public class LiteralKeyPitfall {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> map = new WeakHashMap<>();
String literal = "config"; // string literals are interned: always reachable
String fresh = new String("temp"); // a new, collectable String object
map.put(literal, 1);
map.put(fresh, 2);
literal = null;
fresh = null;
for (int i = 0; i < 50 && map.size() > 1; i++) {
System.gc();
Thread.sleep(20);
}
System.out.println("Remaining keys: " + map.keySet());
}
}
Remaining keys: [config]
Values that refer to their own keys
Values are held strongly. If a value holds a reference to its key, for example map.put(user, new Profile(user)), the key is always reachable through the map itself and is never removed. Wrap such values in a WeakReference, or avoid the back-reference.
Equality-based keys
WeakHashMap compares keys with equals(). If a key is collected, a new but equal object can no longer find the old entry. WeakHashMap works best with keys that use identity-based equals(), such as most framework objects.
7. Practical Use: Metadata for Objects You Do Not Own
Suppose you want to attach a label to UI components created by someone else. A HashMap would keep every component alive forever; a WeakHashMap lets discarded components go:
import java.util.Map;
import java.util.WeakHashMap;
public class ObjectMetadata {
// Extra information about objects we do not own, without keeping them alive
private static final Map<Object, String> labels = new WeakHashMap<>();
static void label(Object o, String text) { labels.put(o, text); }
static String labelOf(Object o) { return labels.getOrDefault(o, "(none)"); }
public static void main(String[] args) throws InterruptedException {
Object button = new Object();
Object dialog = new Object();
label(button, "OK button");
label(dialog, "Settings dialog");
System.out.println("button -> " + labelOf(button));
System.out.println("tracked objects: " + labels.size());
dialog = null; // the dialog is closed and discarded
for (int i = 0; i < 50 && labels.size() > 1; i++) {
System.gc();
Thread.sleep(20);
}
System.out.println("tracked objects after GC: " + labels.size());
System.out.println("button still labelled: " + labelOf(button)); // button is still in use
}
}
button -> OK button tracked objects: 2 tracked objects after GC: 1 button still labelled: OK button
Other uses include listener registries, per-class or per-classloader caches, and canonicalizing maps. For general caches that should expire by size or time, a dedicated cache library is a better choice.
8. Methods
WeakHashMap has the standard Map methods and no extra public methods:
| Method | Note |
|---|---|
put, get, remove, containsKey | O(1) average; each call also clears stale entries. |
size(), isEmpty() | Can decrease between calls as keys are collected. |
keySet(), values(), entrySet() | Iterators are fail-fast; entries may vanish between iterations. |
putIfAbsent, merge, compute*, forEach | Inherited default methods. |
Constructors match HashMap: no-argument (capacity 16, load factor 0.75), with capacity, with capacity and load factor, and from another map.
9. WeakHashMap vs HashMap
| Feature | WeakHashMap | HashMap |
|---|---|---|
| Key references | Weak | Strong |
| Entries removed automatically | Yes, after key is collected | Never |
Stable size() | No | Yes |
| Treeified buckets | No | Yes (Java 8+) |
| Cloneable / Serializable | No | Yes |
| Main use | Metadata and caches tied to object lifetime | General key–value storage |
10. Thread Safety
WeakHashMap is not synchronized. Wrap it with Collections.synchronizedMap(new WeakHashMap<>()) when several threads use it. There is no concurrent weak map in the JDK; caching libraries such as Caffeine offer weak keys with full concurrency.
11. Summary
- WeakHashMap holds keys weakly, so unused keys can be garbage-collected and their entries removed.
- Values are held strongly; values must not refer back to their keys.
- Avoid string literals, small cached integers and enums as keys; they never become unreachable.
- Stale entries are cleared lazily during later map operations.
- It is useful for object metadata and listener registries, and is not thread-safe.
Related Tutorials
- HashMap in Java
- IdentityHashMap in Java
- Garbage Collection in Java
- Difference between HashMap and WeakHashMap
Reference: Java SE 21 API: java.util.WeakHashMap
Frequently Asked Questions (FAQ)
Q1. What is a WeakHashMap in Java?
WeakHashMap is a Map implementation in java.util that holds its keys through weak references. When a key is no longer strongly referenced anywhere else, it can be garbage-collected, and its entry is removed from the map automatically.
Q2. What is a weak reference?
A reference that does not prevent garbage collection. If an object is reachable only through weak references, the garbage collector may reclaim it at its next run. In Java it is represented by java.lang.ref.WeakReference.
Q3. What is the difference between WeakHashMap and HashMap?
HashMap holds keys strongly, so entries stay until you remove them. WeakHashMap holds keys weakly, so entries disappear after their keys are garbage-collected. As a result, WeakHashMap's size can change on its own.
Q4. Are WeakHashMap values weak as well?
No, only the keys are weak. Values are strong references, so a value that refers to its own key keeps that key alive forever.
Q5. When are entries removed from a WeakHashMap?
After the garbage collector has collected the key, the entry is placed in a reference queue. The map removes such entries the next time you call one of its methods, such as get(), put() or size().
Q6. Why are my String keys never removed from a WeakHashMap?
String literals are interned in the string pool and stay strongly reachable, so they are never collected. The same is true for cached Integer values between −128 and 127, for enum constants and for any key you still reference elsewhere.
Q7. Can WeakHashMap be used as a cache?
Only for data whose lifetime should follow the key object, such as metadata about a component. It is not a good general-purpose cache: entries vanish as soon as keys are unused, regardless of how useful they would be. Use SoftReference values or a cache library for that.
Q8. Does WeakHashMap allow null keys and values?
Yes, one null key and any number of null values. The null key is stored as a special internal object and is never collected.
Q9. Is WeakHashMap thread-safe?
No. Use Collections.synchronizedMap(new WeakHashMap<>()) for shared access.
Q10. How does WeakHashMap help prevent memory leaks?
When you store extra data about objects in a normal map and forget to remove it, those objects can never be collected, which is a leak. With a WeakHashMap, the entries go away together with the objects, so you do not need explicit clean-up.
Q11. Why can size() change without modifying the map?
The garbage collector can clear keys at any time, and size() first removes stale entries. Two consecutive calls can therefore return different numbers.
Q12. What is the role of ReferenceQueue in WeakHashMap?
Every entry's weak reference is registered with a ReferenceQueue. When the GC clears a key, it enqueues the entry, and the map uses the queue to find and remove stale entries efficiently.
Q13. What is the difference between WeakHashMap and IdentityHashMap?
WeakHashMap uses weak keys and compares them with equals(). IdentityHashMap uses strong keys and compares them with == (object identity).
Q14. What is the difference between SoftReference and WeakReference?
A softly reachable object is collected only when the JVM is low on memory, so soft references suit memory-sensitive caches. A weakly reachable object is collected at the next garbage collection.
Q15. Is iteration over a WeakHashMap safe?
The iterators are fail-fast for changes you make, but entries can also disappear between iteration steps because of garbage collection. Do not assume an entry seen earlier still exists.
Q16. Does WeakHashMap maintain any order?
No. Like HashMap, its iteration order depends on hash codes and can change.
Q17. Should I call System.gc() to clean a WeakHashMap?
No. System.gc() is only a hint and can hurt performance. Let the JVM collect garbage normally; the map cleans itself up as keys are reclaimed. Tutorials call it only to make the effect visible.
Q18. What are typical real-world uses of WeakHashMap?
Attaching metadata to objects you do not control, listener and observer registries, per-class or per-classloader caches in frameworks, and canonicalizing tables that should not keep their objects alive.
Q19. Can I use a record or a String created with new as a WeakHashMap key?
Yes, any object that becomes unreachable can be collected. But since lookups use equals(), an equal object created later cannot reach an entry whose original key was already collected.
Q20. What is the time complexity of WeakHashMap operations?
get, put and remove are O(1) on average, plus a small cost for clearing stale entries. Buckets are plain linked lists, so heavy collisions degrade lookups to O(n).