2

AWeakHashMap几乎可以与 aWeakReference结合使用ReferenceQueue- 关于此的新闻为零。这是一个关于它应该如何工作的精简示例:

public class ReferenceQueuePlayground {

    public static void main(String[] args) {
        ReferenceQueue<Referent> q = new ReferenceQueue<>();

        Referent ref = new Referent();
        WeakReference<Referent> weak = new WeakReference<>(ref, q);

        ref = null;

        // wait for GC to reclaim Referent
        while (weak.get() != null) {
            System.gc();
        }

        // this might return null, because ReferenceQueue is notified asynchronously
        // but I am assuming the happy path here 
        Reference<? extends Referent> reference = q.poll();

        // this will be false
        System.out.println(reference == null);
        // this will be true
        System.out.println(reference.get() == null);
    }

    @RequiredArgsConstructor
    @Getter
    static class Referent {

    }
}

这正是它WeakHashMap的工作原理——当referent被回收并将reference放在ReferenceQueue. 在一些后续的操作中,expungeStaleEntries被调用,它基本上会从这个元素中ReferenceQueue一个一个地获取元素并对其进行操作。

referent我的问题是:如果现在消失了,它如何“对他们采取行动” ?毕竟这是 a ...HASHMap,所以为了移除元素,它必须知道它是hashCode. 你怎么知道hashCode现在已经消失的东西?

4

1 回答 1

2

有两种方法。第一个是线性搜索

由于所指对象确实已经消失并且您无法计算它,因此您可以在中的所有条目中hashCode搜索Referencewith 。在发布的示例中,您可以添加几行,例如:==Map

    WeakReference<Referent> weak = new WeakReference<>(ref, q);
    // <--- this
    System.out.println(weak);

    ref = null;

    while (weak.get() != null) {
        System.out.println("not yet");
        System.gc();
    }

    Reference<? extends Referent> reference = q.poll();
    // <---- and this
    System.out.println(reference);

这两者都将打印相同的东西,这是完全有道理的。所以理论上, aWeakHashMap可以取reference它得到的(实际上是 a Entry)并遍历它的内部数组,直到找到匹配项。

显然,这会很慢。

第二种方法是WeakHashMap 实际采用的方法。首次创建an 时Entry,它会计算hashCode并将其放入本地字段:

/**
  * Creates new entry.
  */
Entry(Object key, V value,
      ReferenceQueue<Object> queue,
      int hash, Entry<K,V> next) {
    super(key, queue);
    this.value = value;
    this.hash  = hash;
    this.next  = next;
}

此时它知道Key,所以它可以计算hashCodeexpungeStaleEntries稍后调用时:

 private void expungeStaleEntries() {
    for (Object x; (x = queue.poll()) != null; ) {
        synchronized (queue) {
            @SuppressWarnings("unchecked")
                Entry<K,V> e = (Entry<K,V>) x;
            int i = indexFor(e.hash, table.length);

它已经知道hashCode, 因为它是在此之前计算的。它不知道Key,但也不需要它。

这将有助于找到该条目所在的存储桶,但要真正找到特定的条目,它只能==在条目本身上使用。既然Key已经走了,equals那是不可能的,但那无所谓。

于 2020-03-10T16:13:31.163 回答