WeakHashMap的用法是什么?

6

WeakHashMap是Map接口的一种实现,如果相应的键不再被程序的任何部分引用,则值对象的内存可以由垃圾收集器回收。因此,如果键在程序中不再使用,则其Entry对象将被垃圾收集,而不考虑其用途。到这里为止很清楚。

这与HashMap不同,即使键不再被引用,值对象仍然留在HashMap中。我们需要显式调用HashMap对象上的remove()方法来删除该值。调用remove只会从地图中删除条目。它是否已准备好进行GC取决于它是否仍在程序中某处使用。

请查看以下编码示例以解释上述内容

根据我的理解,使用WeakHashMap超过HashMap

我的理解是,我们只有在想确保当键不再被程序的任何部分引用时,才应该使用WeakHashMap。这使程序内存效率高。我的理解在这里是正确的吗?

根据JavaDocs中WeakHashMap的用法,我注意到以下声明:
这个类主要用于键对象的等号测试使用==操作符进行对象标识。我不理解以上声明的含义,也不了解它与我对WeakHashMap用法的理解有何不同。实际上,我不知道这个声明与WeakHashMap的用法有何关系?
更新:在仔细阅读下面的Javadocs声明后,
WeakHashMap中的条目将在其键不再被常规使用时自动删除。更准确地说,给定键的映射的存在不会阻止垃圾收集器丢弃该键,即使该键已被定为最终状态并回收。当一个键被丢弃时,它的条目从映射中被有效地移除,因此该类的行为与其他Map实现略有不同。 我为了自己和他人的利益而修改了我的理解
根据我的修改理解,使用WeakHashMap的用法优于HashMap。
我们只有在希望确保键值对在GC运行时从映射中删除,而除了映射本身,键不再被普通使用时,才应选择WeakHashMap。
例如:
    WeakHashMap<Integer, String> numbers = new WeakHashMap<Integer, String>();
    numbers.put(new Integer(1), "one");// key only used within map not anywhere else
    numbers.put(new Integer(2), "two");
    System.out.println(numbers.get(new Integer(1))); // prints "one"
    System.gc();
    // let's say a garbage collection happens here
    System.out.println(numbers.get(new Integer(1))); // prints "null"
    System.out.println(numbers.get(new Integer(2))); // prints "null"


    Object key = new Object();
    m1.put(key, c1);
    System.out.println(m1.size());
    key = null or new Object() ; // privious key only used within map not anywhere else
    System.gc();
    Thread.sleep(100);
    System.out.println(m1.size());
5个回答

2

这是因为当对象不再被程序的任何其他部分强引用时,它们将被垃圾回收(GC)。

如果我们有一个 WeakHashMap<MyObject, String>,那么如果我们执行以下操作:

MyObject mo = new MyObject();
map.put(mo, "Test");
mo = null;

然后,条目mo -> Test 将有资格进行垃圾回收。 这意味着,如果您拥有一个自定义的 .equals 实现,该实现使用 MyObject 的某个属性来测试相等性,那么您以后就不能再这样做:

MyObject mo2 = new MyObject();
map.get(mo2);

因为即使您重写了.equals方法,使得mo2.equals(mo) == true,但并不意味着mo2 == mo,因此该条目可能已经被垃圾回收掉了。
关键在于,如果您保留对mo的引用并使用它从Map中检索值,则必须确保该引用== mo,因此有两件事情是真实的:
1. 条目mo -> Test无法被垃圾回收 2. 您可以使用基于==.equals方法从地图中检索条目
基本上,由于GC将使用强引用来测试对象是否可以被回收,因此最好确保您的.equals方法也是如此,以避免混淆。

1
文档表示这段代码并不是很有用:
WeakHashMap<Integer, String> numbers = new WeakHashMap<Integer, String>();
numbers.put(new Integer(1), "one");
numbers.put(new Integer(2), "two");
System.out.println(numbers.get(new Integer(1))); // prints "one"
// let's say a garbage collection happens here
System.out.println(numbers.get(new Integer(1))); // prints "null"
System.out.println(numbers.get(new Integer(2))); // prints "null"

这会发生在任何可以相等的不同实例的类中。Javadoc只是在提醒你,以防你还没有注意到,这并没有帮助。

1
运行这个测试。
    Object key = new Object();
    WeakHashMap m = new WeakHashMap();
    m.put(key, 1);
    System.out.println(m.size());
    key = null;
    System.gc();
    Thread.sleep(100);
    System.out.println(m.size());

尽管System.gc不能保证运行GC,但在我的Oracle JVM 7上它总是运行,并且此测试会打印输出。
1
0

这意味着GC从地图中删除了条目,因为键没有被任何其他地方引用,只有地图本身引用了它。

0

在 Evgeniy Dorofeev 的回答基础上,这里提供一个带有一些注意事项的示例:

import java.util.Map;
import java.util.WeakHashMap;

public class WeakHashMapTest {

    public static void main(String[] args) throws InterruptedException {
        String key = new String();
        Map<String, String> m = new WeakHashMap();

        m.put(key, "value");
        System.out.println(m.size());

        m.put("key", "value");
        System.out.println(m.size());

        m.put(new String(), "value");
        System.out.println(m.size());

        m.put(new String("k"), "value");
        System.out.println(m.size());

        key = null;
        System.gc();
        Thread.sleep(100);

        System.out.println(m.size());
    }
}

输出

1
2
2
3
1

0

我认为这意味着检测对象是否相等时要使用对象在内存中的位置,而不仅仅是因为两个不同的实例恰好在值上“相等”。

如果不是这种情况,那么很难确定该键是否被引用,可能会引用并用于查找值的是一个不同的实例,虽然它们的值是“相等”的。

如果说明键必须具有引用相等性,可能会更清晰,这样你就会知道键是否不再被任何WeakHashMap实例之外的东西引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接