What WeakHashMap is good for

From Spivey's Corner
Jump to: navigation, search

The java class WeakHashMap<K, V> represents mappings from keys K to values V in the same way as the other classes in the Java library that represent mappings. But in each (k, v) pair, it keeps only a weak reference to the key k, and it is implemented in such a way that if k disappears because of garbage collection, then the cell that formerly represented the pair (k, v) is removed from the mapping.

WeakHashMap as an attribute mapping – Yes

That makes WeakHashMap an ideal class for associating attributes with objects – without modifying the objects themselves to add more instance variables. The weak references mean that the objects (playing the role of keys in the mapping) are not prevented from being garbage collected just because the table is associating some attributes with them, so that as soon as the rest of the program forgets about an object, the attribute table can forget about it too.

WeakHashMap as a unique instance table – No

What WeakHashMap does not do well is to act as a cache or table of unique instances, because that needs a table where the values and not the keys are held as weak references. Suppose that we wanted to store Font objects uniquely in our program, so that there was only one Font object with a given name. In that case, instead of creating Font objects at will, we might keep a table mapping names to fonts, so that we could find a Font object with a given name if one had already been created. There are several reasons we might do this:

  • to save the space that would be occupied by keeping multiple copies of essentially the same Font object,
  • to save the time needed to compute multiple Font objects when one would do, and
  • to allow us to compare two Font objects for equality by comparing their identities, not by the more expensive process of comparing their names character by character.

Assuming that the other attributes of a Font object could be recomputed from its name, we could prevent a space leak by putting only a weak reference to the object into the table, so that the garbage collector could collect the object when it was no longer needed by the rest of the program. Next time a Font with that name was needed, we could make a new one, and the behaviour of weak references would ensure that the new font was again the only one in existence with the given name.

But this behaviour (weak references to values) is precisely what WeakHashMap doesn't give you, and trying to use it for this purpose is wrong. If we add a new entry to the table with

table.put(name, new Font(name));

then neither the entry nor the Font object will ever be deleted: the entry in the hash table contains a strong reference to the Font object, which contains a strong reference to the name. Thus, the name is strongly reachable, and the weak reference to it from the hash table entry will never be destroyed.

If, on the other hand, we add a new entry with

table.put(new String(name), new Font(name));

then there is only the one (weak) reference to the newly created String object, and that and the hash table entry can be deleted immediately, even if the Font object is still in use. That violates the requirement that there be only one extant Font object with a given name.

It doesn't really help to wrap the Font object in another weak reference, like this:

table.put(name, new WeakReference<Font>(new Font(name)));

Sure enough, while the Font object remains strongly reachable, so will the string name, and that will keep the hash table entry in existence; and when the Font is no longer strongly reachable, that object can itself be reclaimed. But whether the entry in the table is deleted at that point will depend on whether the String object name has any other strong references to it. And worse, if the Font object is modified so that, instead of keeping a reference to its name, it recomputes the name when needed, then name may cease to be strongly reachable even while the Font object is still in use, and then the table (as before) does not guarantee uniqueness. String is an immutable class, so we ought not to need to worry whether we are dealing with two equal instances or a single, shared instance. But the behaviour of the table makes that matter.

If we're going to wrap the Font object in a weak reference, it would be better to use an ordinary HashMap instead of a WeakHashMap, because then the worry about disappearing entries would not arise. Of course, that does not solve the problem of deleting hash table entries when they become useless. For that, a new and different hash table class is needed.