为什么Java类WeakReference没有覆盖hashcode和equals方法?

3
我原本期望WeakReference类像这样覆盖hashCode和equals方法
class WeakReference<T>{
  T ref;

  int hashCode(){
    return ref.hashCode();
  }

  boolean equals(Object o){
    return ref.equals(o);
  }
}

这样我就可以直接将We WeakReference用作哈希映射中的键,例如:

Person p1 = new Person("p1");
WeakReference<Person> wr = new WeakReference<Person>(p1);

map.put(wr, "some value object");

但是当我进行测试时,我发现hashCode和equals方法没有被覆盖重写。

Person p1 = new Person("p1");
WeakReference<Person> wr = new WeakReference<Person>(p1);
WeakReference<Person> wr2 = new WeakReference<Person>(p1);

System.out.println(wr.hashCode()); // prints x
System.out.println(wr2.hashCode()); // prints y

System.out.println(wr.equals(wr2)); // prints false

为什么WeakReference类中没有重写hashCode和equals方法呢?是否有特定原因?


因为没有必要,因为 WeakHashMap 可以满足你的需求。 - Andreas
2
如果WeakReference重写了equalshashCode,那么将它们用作HashMap键将是一个非常糟糕的想法。这是因为当所持有的对象被取走时,WeakReferencehashCode几乎肯定会改变。您永远不应该导致HashMap键在放入后发生更改,因为这样就无法再找到它了。 - Paul Boddington
那么...每个收集到的密钥都等同于其他收集到的密钥?这是一个非常糟糕的想法... - Holger
2个回答

4
在Map的任何键(或Set的元素)中,一个重要的方面是它必须是不可变的(或者至少在添加到集合后不会改变)。更改键具有未定义的行为,这极有可能无法正常工作。
WeakReference由于执行GC而可以随时更改,即以您无法控制的方式进行更改,这使得equals / hashCode不适用于使用这些内容的一般集合。
一个简单的方法是拥有一个WeakHashMap数组,例如32个分区。使用hashCode()来确定要使用哪个WeakHashMap。这样,您可以同时访问每个单独的WeakHashMap的线程(最佳情况)。
随着更多的并发性,您可以增加分区的数量。

1
没问题。弱引用可以在其构造函数中计算哈希码并缓存它,因此它永远不会改变。 - Mike Nakis
2
@MikeNakis 无论如何,它如何缓存equals的信息? - Peter Lawrey
实际上,我试图通过包装ConcurrentHashMap并在弱引用内部包装/取消包装映射键来创建MyWeakConcurrentHashMap。消费者无需知道这一点。如果弱引用按照我的期望实现了hashCode和equals,那么它应该可以正常工作。 - Mangoose
1
@Mangoose,虽然这是不可能的,因为WeakReference随时可能会改变,所以你需要以不同的方式处理这样的键。 - Peter Lawrey
2
@PeterLawrey和Mike,感谢你们进行这场精彩的讨论并澄清了观点。 - Mangoose

0
似乎 WeakHashMap 使用扩展了 WeakReferenceEntry,并重写了 hashCodeequals,您可以查看它们的实现。
  /**
     * The entries in this hash table extend WeakReference, using its main ref
     * field as the key.
     */
    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;

        /**
         * 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;
        }

        @SuppressWarnings("unchecked")
        public K getKey() {
            return (K) WeakHashMap.unmaskNull(get());
        }

        public V getValue() {
            return value;
        }

        public V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            K k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                V v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public int hashCode() {
            K k = getKey();
            V v = getValue();
            return Objects.hashCode(k) ^ Objects.hashCode(v);
        }

        public String toString() {
            return getKey() + "=" + getValue();
        }
    }

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