• Java集合包(八)——Map实现类之 WeakHashMap 原理分析


    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/13575843.html 

    、WeakHashMap的重要成员

        1)table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。

          WeakHashMap的存储原理与HashMap是一样的,都是通过“链表+红黑树”的策略。

          不同的地方在于Entry的实现。

        private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
            private V value;
            private final int hash;
            // 指向下一个节点
            private Entry<K,V> next;
    
            // 构造函数。
            Entry(K key, V value,
              ReferenceQueue<K> queue,
                  int hash, Entry<K,V> next) {
                super(key, queue);
                this.value = value;
                this.hash  = hash;
                this.next  = next;
            }
    
            public K getKey() {
                return WeakHashMap.<K>unmaskNull(get());
            }
        
            ......
    }

        WeakHashMap的Entry继承了弱引用 WeakReference,在Entry的构造函数中,它将key和WeakHashMap的引用队列queue作为参数,调用父类WeakReference的构造函数创建了一个弱引用,也就是说:key是作为弱引用的形式存在的。

        WeakReference的源码:

    public class WeakReference<T> extends Reference<T> {
        public WeakReference(T referent) {
            super(referent);
        }
        
        public WeakReference(T referent, ReferenceQueue<? super T> q) {
            super(referent, q);
        }
        
    }

        

        2)queue保存的是“已被GC清除”的“弱引用的键”队列。

    private final ReferenceQueue<K> queue = new ReferenceQueue<K>();

        弱引用类型的key的创建,需要传递 ReferenceQueue 作为构造参数,用它作为容器:如果弱引用key 所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

        然后这个队列中的key所对应的键值对就会在下一次Map扩容的时候被删除掉。

        3)其他成员与HashMap一样,例如:size、加载因子等。

    二、关键函数

        1、清空table中的无用键值对:回收key已经没有在其他地方被引用的键值对。

        // 清空table中无用键值对。原理如下:
    109     // (01) 当WeakHashMap中某个“弱引用的key”由于没有再被引用而被GC收回时,
    110     //   被回收的“该弱引用key”也被会被添加到"ReferenceQueue(queue)"中。
    111     // (02) 当我们执行expungeStaleEntries时,
    112     //   就遍历"ReferenceQueue(queue)"中的所有key
    113     //   然后就在“WeakReference的table”中删除与“ReferenceQueue(queue)中key”对应的键值对
    114     private void expungeStaleEntries() {
    115         Entry<K,V> e;
    116         while ( (e = (Entry<K,V>) queue.poll()) != null) {
    117             int h = e.hash;
    118             int i = indexFor(h, table.length);
    119 
    120             Entry<K,V> prev = table[i];
    121             Entry<K,V> p = prev;
    122             while (p != null) {
    123                 Entry<K,V> next = p.next;
    124                 if (p == e) {
    125                     if (prev == e)
    126                         table[i] = next;
    127                     else
    128                         prev.next = next;
    129                     e.next = null;  // Help GC
    130                     e.value = null; //  "   "
    131                     size--;
    132                     break;
    133                 }
    134                 prev = p;
    135                 p = next;
    136             }
    137         }
    138     }

      2、put函数:在当前大小触及阈值时时扩容。

    // 将“key-value”添加到WeakHashMap中
    195     public V put(K key, V value) {
    196         K k = (K) maskNull(key);
    197         int h = HashMap.hash(k.hashCode());
    198         Entry[] tab = getTable();
    199         int i = indexFor(h, tab.length);
    200 
    201         for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
    202             // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
    203             if (h == e.hash && eq(k, e.get())) {
    204                 V oldValue = e.value;
    205                 if (value != oldValue)
    206                     e.value = value;
    207                 return oldValue;
    208             }
    209         }
    210 
    211         // 若“该key”对应的键值对不存在于WeakHashMap中,则将“key-value”添加到table中
    212         modCount++;
    213         Entry<K,V> e = tab[i];
    214         tab[i] = new Entry<K,V>(k, value, queue, h, e);
    215         if (++size >= threshold)
    216             resize(tab.length * 2);
    217         return null;
    218     }

        

        3、扩容函数:将容量放大、调用清理函数删除无用的键值对。

    // 重新调整WeakHashMap的大小,newCapacity是调整后的单位
    221     void resize(int newCapacity) {
    222         Entry[] oldTable = getTable();
    223         int oldCapacity = oldTable.length;
    224         if (oldCapacity == MAXIMUM_CAPACITY) {
    225             threshold = Integer.MAX_VALUE;
    226             return;
    227         }
    228 
    229         // 新建一个newTable,将“旧的table”的全部元素添加到“新的newTable”中,
    230         // 然后,将“新的newTable”赋值给“旧的table”。
    231         Entry[] newTable = new Entry[newCapacity];
    232         transfer(oldTable, newTable);
    233         table = newTable;
    234 
    235         if (size >= threshold / 2) {
    236             threshold = (int)(newCapacity * loadFactor);
    237         } else {
    238             // 删除table中“已被GC回收的key对应的键值对”
    239             expungeStaleEntries();
    240             transfer(newTable, oldTable);
    241             table = oldTable;
    242         }
    243     }

      

    三、WeakHashMap的键是“弱键”的真正理解

        首先我们要知道,Map接口的key都是引用类型,必须实现了hashCode和equals方法,不能是基本类型。

        因此,键值对中的键是引用类型来的,它指向一个对象。

        Java中的引用分为:强、软、弱、虚。【具体参见另一篇博文:https://www.cnblogs.com/ygj0930/p/6514114.html

        WeakHashMap的键是 弱引用 类型的,弱引用类型的特征是:如果一个对象只具有弱引用,则当GC扫描到该对象时就会回收它,不必等到内存不够时才回收;在回收之前我们可以通过get()方法获取对象,如果回收之后再调用get()方法就会返回null。

        因此,WeakHashMap的键是“弱键”的真正解读应该为:用来作为key的引用类型值所指向的对象,如果只剩下weakHashMap中的键值对中的键指向它,而在其他地方再无别的引用指向它时,则该对象被GC扫描到会被回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中【弱引用创建时可以传递一个引用队列进来】,而体现在weakHashMap则会被添加到WeakHashMap实例的 ReferenceQueue 队列中。当weakHashMap的当前大小触及阈值调用resize进行扩容时,就会调用 expungeStaleEntries() 函数,在该函数中遍历ReferenceQueue 队列,逐个删除weakHashMap中与引用队列中的key相对应的键值对。

  • 相关阅读:
    FreeMarker配置详解
    tab显示不同数据
    EL表达式可以直接放在url的“ ”里面
    js的搜索框
    js实现tab页面不同内容切换显示
    如何让html中的td文字只显示部分
    MobileNets: Open-Source Models for Efficient On-Device Vision
    LFW Face Database下载
    python遍历文件夹
    把cifar数据转换为图片
  • 原文地址:https://www.cnblogs.com/ygj0930/p/13575843.html
Copyright © 2020-2023  润新知