If I iterate over the key set of a WeakHashMap, do I need to check for null values?
WeakHashMap<MyObject, WeakReference<MyObject>> hm
= new WeakHashMap<MyObject, WeakReference<MyObject>>();
for ( MyObject item : hm.keySet() ) {
if ( item != null ) { // <- Is this test necessary?
// Do something...
}
}
In other words, can elements of the WeakHashMap be collected while I am iterating over them?
EDIT
For the sake of this question, one can assume that no null
entries is added in the hash map.
Again from WeakHashMap javadoc:
Which I read as: Yep... When there are no remaining external references to a Key in WeakHaskMap, then that Key maybe GC'd, making the associated Value unreachable, so it to (presuming there are no external references directly to it) is elligible for GC.
I'm going to test this theory. It's only my interpretation of the doco... I don't have any experience with WeakHashMap... but I immediately see it's potential as "memory-safe" object-cache.
Cheers. Keith.
EDIT: Exploring WeakHashMap... specifically testing my theory that an external-references to the particular key would cause that key to be retained... which is pure bunkum ;-)
My test harness:
The (rathering perplexing, I think) results of one test-run:
It would appear that keys are still disappearing WHILE my code is executing... possibly a micro-sleep is required after the GC-hint... to give the GC time to do it's stuff. Anyway, this "volatility" is interesting behaviour.
EDIT 2: Yup, adding the line
try{Thread.sleep(10);}catch(Exception e){}
directly after theSystem.gc();
makes the results "more predictable".Hmmm... A cache that just completely disappears when the GC kicks in... at arbitrary times in a real app... not much use... Hmmm... What is WeakHashMap for I wonder? ;-)
Last EDIT, I promise
Here's my krc/utilz/Random (used in the above test)
Assuming that you don't insert a
null
key value into aWeakHashMap
, you do not need to check whether the key value of the iteration isnull
when iterating through the key set. You also do not need to check whether getKey() called on theMap.Entry
instance of the iteration isnull
when iterating through the entry set. Both are guaranteed by the documentation, but it's somewhat indirect; it is the contract of Iterator.hasNext() that provides these guarantees.The JavaDoc for
WeakHashMap
states:The JavaDoc for Iterator.hasNext() states:
Because the key set and entry set views satisfy the
Set
contract (as required by theMap
contract whichWeakHashMap
implements), the iterators returned by the Set.iterator() method must satisfy theIterator
contract.When hasNext() returns
true
, theIterator
contract requires that the next call to next() on theIterator
instance must return a valid value. The only way forWeakHashMap
to satisfy theIterator
contract is for the hasNext() implementation to keep a strong reference to the next key when it returnstrue
, thereby preventing the weak reference to the key value held by theWeakHashMap
from being cleared by the garbage collector, and, as a consequence, preventing the entry from being automatically removed from theWeakHashMap
so that next() has a value to return.Indeed, if you look at the source of
WeakHashMap
, you will see that theHashIterator
inner class (used by the key, value, and entry iterator implementations) has acurrentKey
field that holds a strong reference to the current key value and anextKey
field that holds a strong reference to the next key value. ThecurrentKey
field allowsHashIterator
to implement Iterator.remove() in full compliance with that method's contract. ThenextKey
field allowsHashIterator
to satisfy the contract of hasNext().That being said, suppose you want to gather up all key values in the map by calling toArray(), and then iterate through this snapshot of key values. There are a few cases to consider:
If you call the no-argument toArray() method that returns
Object[]
or pass in a zero-length array, as in:.. then you do not need to check whether
item
isnull
because in both cases, the array returned will be trimmed to hold the exact number of elements that were returned by the iterator.If you pass in an array of length >= the
WeakHashMap
's then-current size, as in:.. then the
null
check is necessary. The reason is that, in the time between when size() returned a value (which is used to create the array to store the keys) and toArray() finishes iterating through the keys of theWeakHashMap
, an entry may have been automatically removed. This is the "room to spare" case mentioned in the JavaDoc for Collection.toArray():Because you know that you have not inserted a
null
key value into theWeakHashMap
, you can break upon seeing the firstnull
value (if you see anull
).A slight variant of the previous case, if you pass in an array of non-zero length, then you need the
null
check for the reason that the "room to spare" case might happen at runtime.I'm not familiar with
WeakHashMap
, but you might have one null object. see this example:From the WeakHashMap documentation, the key that is placed into the hash map is of a templated type, which means it is inherited from java.lang.object. As a result, it may be null. So, a key may be null.