I'm trying to use Java7's WeakHashMap and I found its isEmpty() method give me wrong results.
import java.util.Map;
import java.util.WeakHashMap;
public class Test
{
public static void main(final String[] args)
{
final Map<String, Boolean> map = new WeakHashMap<>();
String b = new String("B");
map.put(b, true);
b = null;
System.gc();
System.out.println(map.isEmpty());
System.out.println(map.keySet().isEmpty());
System.out.println(map);
}
}
The actual result:
false
true
{}
That is to say,
map.isEmpty() and map.keySet().isEmpty() is not consistent. Can someone help me to understand it? Thanks a lot.
You should read the javadoc of WeakHashMap
:
The behavior of the
WeakHashMap
class depends in part upon the actions of the garbage collector, so several familiar (though not required)Map
invariants do not hold for this class. Because the garbage collector may discard keys at any time, aWeakHashMap
may behave as though an unknown thread is silently removing entries. In particular, even if you synchronize on aWeakHashMap
instance and invoke none of its mutator methods, it is possible for thesize
method to return smaller values over time, for theisEmpty
method to returnfalse
and thentrue
, for thecontainsKey
method to returntrue
and laterfalse
for a given key, for theget
method to return a value for a given key but later returnnull
, for theput
method to returnnull
and theremove
method to returnfalse
for a key that previously appeared to be in the map, and for successive examinations of the key set, the value collection, and the entry set to yield successively smaller numbers of elements.
The short of all that is the the effects you've seen are entirely valid.
WeakHashMap::isEmpty says:
...This result is a snapshot, and may not reflect unprocessed entries that will be removed before next attempted access because they are no longer referenced.
So you would expect that isEmpty()
returns the correct value after GC and after access. This code demonstrates this:
public class Scratch1 {
public static void main(final String[] args) {
final Map<String, Boolean> map = new WeakHashMap<>();
String b = new String("B");
map.put(b, true);
b = null;
System.gc();
// map not internally accessed at this point
System.out.println(map.isEmpty());
// let's access the Map's internals (and hopefully coerce
// it into removing no-longer-referenced keys)
System.out.println(map.keySet()
.isEmpty());
// map HAS now been accessed
System.out.println(map.isEmpty());
}
}
Yields:
false
true
true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With