• Java容器解析系列(13) WeakHashMap详解


    关于WeakHashMap其实没有太多可说的,其与HashMap大致相同,区别就在于:

    1. 对每个key的引用方式为弱引用;

      关于java4种引用方式,参考java Reference

      网上很多说 弱引用指向 Entry,这种说法是完全错误的

    2. key被回收时,对应的value并没有回收,只有在调用WeakHashMap的方法时才会回收value;

    具体请看下列源码解析:

    /**
     * 数据结构原理几乎与HashMap一致;
     * WeakHashMap 不会阻止Entry.key被回收;
     * WeakHashMap中的每个key都是通过weakreference引用;
     * 
     * expuntgeStaleEntries():清除 key已被回收 的entry,进而entry.value也被回收
     * WeakHashMap中所有的public的增删改查方法都直接或间接调用了expuntgeStaleEntries()方法;
     *
     * @since 1.2
     */
    public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
    
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
        private static final int MAXIMUM_CAPACITY = 1 << 30;
        private static final float DEFAULT_LOAD_FACTOR = 0.75f;
        Entry<K, V>[] table;
        private int size;
        private int threshold;
        private final float loadFactor;
    
        // 当key被回收时,其Entry(继承自WeakReference)对象会被添加到该队列中
        private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
        int modCount;
        static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
        transient boolean useAltHashing;
        transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
    
        // 各种构造器,与HashMap没有太大区别,hash算法相关代码,省略......
    
        // 清除那些key已经被回收的entry
        // WeakHashMap 中所有的public的增删改查方法都直接或间接调用了expuntgeStaleEntries()方法
        // 如果在添加元素到 WeakHashMap 后,如果不再调用任何该 WeakHashMap 的方法,
        // 那么 Entry 不会被回收,进而 Entry.value 不会被回收
        private void expungeStaleEntries() {
            // weakreference被回收时,会被添加到其注册的referencequeue中
            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);
    
                    Entry<K, V> prev = table[i];
                    Entry<K, V> p = prev;
                    while (p != null) {
                        Entry<K, V> next = p.next;
                        if (p == e) {
                            if (prev == e)
                                table[i] = next;
                            else
                                prev.next = next;
                            // 既然key被清除了,这里把value也清除掉(帮助GC)
                            // 其实这里也可以不清除value,因为这里将entry从链表中移除了,等于entry被删除了,虚拟机在之后的某个时间会将entry回收掉
                            e.value = null; // Help GC
                            size--;
                            break;
                        }
                        prev = p;
                        p = next;
                    }
                }
            }
        }
    
        private Entry<K, V>[] getTable() {
            expungeStaleEntries();
            return table;
        }
    
        public int size() {
            if (size == 0)
                return 0;
            expungeStaleEntries();
            return size;
        }
    
        public V get(Object key) {
            Object k = maskNull(key);
            int h = hash(k);
            Entry<K, V>[] tab = getTable();
            int index = indexFor(h, tab.length);
            Entry<K, V> e = tab[index];
            while (e != null) {
                if (e.hash == h && eq(k, e.get()))
                    return e.value;
                e = e.next;
            }
            return null;
        }
    
        public boolean containsKey(Object key) {
            return getEntry(key) != null;
        }
    
        Entry<K, V> getEntry(Object key) {
            Object k = maskNull(key);
            int h = hash(k);
            Entry<K, V>[] tab = getTable();
            int index = indexFor(h, tab.length);
            Entry<K, V> e = tab[index];
            while (e != null && !(e.hash == h && eq(k, e.get())))
                e = e.next;
            return e;
        }
    
        public V put(K key, V value) {
            Object k = maskNull(key);
            int h = hash(k);
            Entry<K, V>[] tab = getTable();// getTable() 调用了 expungeStaleEntries()
            int i = indexFor(h, tab.length);
    
            for (Entry<K, V> e = tab[i]; e != null; e = e.next) {
                if (h == e.hash && eq(k, e.get())) {
                    V oldValue = e.value;
                    if (value != oldValue)
                        e.value = value;
                    return oldValue;
                }
            }
            modCount++;
            Entry<K, V> e = tab[i];
            tab[i] = new Entry<>(k, value, queue, h, e);
            if (++size >= threshold)
                resize(tab.length * 2);
            return null;
        }
    
        // 与HashMap极度相似的代码,省略......
    
        public V remove(Object key) {
            Object k = maskNull(key);
            int h = hash(k);
            Entry<K, V>[] tab = getTable();
            int i = indexFor(h, tab.length);
            Entry<K, V> prev = tab[i];
            Entry<K, V> e = prev;
    
            while (e != null) {
                Entry<K, V> next = e.next;
                if (h == e.hash && eq(k, e.get())) {
                    modCount++;
                    size--;
                    if (prev == e)
                        tab[i] = next;
                    else
                        prev.next = next;
                    return e.value;
                }
                prev = e;
                e = next;
            }
    
            return null;
        }
    
        public void clear() {
            // 清空队列中的所有weakreference
            while (queue.poll() != null)
                ;
    
            modCount++;
            // 清空所有Entry
            Arrays.fill(table, null);
            size = 0;
    
            while (queue.poll() != null)
                ;
        }
    
        // Entry为WeakReference的子类,作为弱引用指向key
        private static class Entry<K, V> extends WeakReference<Object> implements Map.Entry<K, V> {
            V value;
            int hash;
            Entry<K, V> next;
    
            Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K, V> next) {
                super(key, queue);// 这里就确定了该 WeakReference 指向 key (referent 变量值为 key)
                this.value = value;// 对 value 的引用为强引用,所以必须手动回收(通过 expungeStaleEntries())
                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 ((k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode()));
            }
    
            public String toString() {
                return getKey() + "=" + getValue();
            }
        }
    
        // 各种迭代器,遍历方式,省略......
    
    }
    
  • 相关阅读:
    hdu.1226.超级密码(bfs)
    zstu.2512. Moving Tables(贪心)
    zoj.3868.GCD Expectation(数学推导>>容斥原理)
    acdream.LCM Challenge(数学推导)
    acdream.Triangles(数学推导)
    acdream.A Very Easy Triangle Counting Game(数学推导)
    acdream.Bet(数学推导)
    acdream.郭式树(数学推导)
    EntityFramework(转自wiki)
    石墨烯(转自wiki)
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/10935580.html
Copyright © 2020-2023  润新知