4

我正在尝试使用 Java7 的 WeakHashMap,但我发现它的 isEmpty() 方法给了我错误的结果。

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);
    }

}

实际结果:

错误的

真的

{}

也就是说,

map.isEmpty() 和 map.keySet().isEmpty() 不一致。有人可以帮我理解吗?非常感谢。

4

2 回答 2

7

您应该阅读以下的 javadoc WeakHashMap

类的行为WeakHashMap部分取决于垃圾收集器的行为,因此一些熟悉的(尽管不是必需的)Map不变量不适用于此类。因为垃圾收集器可能随时丢弃键,aWeakHashMap可能表现得好像一个未知线程正在默默地删除条目。特别是,即使您在一个WeakHashMap实例上同步并且不调用它的任何 mutator 方法,该方法也有可能size随着时间的推移返回较小的值,该isEmpty方法返回false,然后truecontainsKey方法返回true,稍后返回false给定的键, 用于get为给定键返回值但稍后返回null的方法put返回方法nullremove返回false以前出现在映射中的键的方法,以及对键集、值集合和条目集的连续检查以产生连续较少数量的元素。

总而言之,您所看到的效果是完全有效的。

于 2019-03-28T19:59:14.183 回答
2

WeakHashMap::isEmpty说:

...此结果是一个快照,可能不会反映在下次尝试访问之前将被删除的未处理条目,因为它们不再被引用。

所以你会期望isEmpty()在 GC访问之后返回正确的值。此代码演示了这一点:

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());
    }

}

产量:

false
true
true
于 2019-03-28T20:04:15.770 回答