zoukankan      html  css  js  c++  java
  • WeakHashMap源码分析

    WeakHashMap是一种弱引用map,内部的key会存储为弱引用,

    当jvm gc的时候,如果这些key没有强引用存在的话,会被gc回收掉,

    下一次当我们操作map的时候会把对应的Entry整个删除掉,基于这种特性,WeakHashMap特别适用于缓存处理。

    //初始化容量
        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;
    
       //引用队列
        private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    
        //修改次数
        int modCount;

    (1)容量

    容量为数组的长度,亦即桶的个数,默认为16,最大为2的30次方,当容量达到64时才可以树化。

    (2)装载因子

    装载因子用来计算容量达到多少时才进行扩容,默认装载因子为0.75。

    (3)引用队列

    当弱键失效的时候会把Entry添加到这个队列中,当下次访问map的时候会把失效的Entry清除掉。

    (4 ) entry为内部类

    构造方法

     public WeakHashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
    
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal Load factor: "+
                                                   loadFactor);
            int capacity = 1;
            while (capacity < initialCapacity)
                capacity <<= 1;
            table = newTable(capacity);
            this.loadFactor = loadFactor;
            threshold = (int)(capacity * loadFactor);
        }
    
      
        public WeakHashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }
    
        public WeakHashMap() {
            this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
        }
    
    
        public WeakHashMap(Map<? extends K, ? extends V> m) {
            this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                    DEFAULT_INITIAL_CAPACITY),
                 DEFAULT_LOAD_FACTOR);
            putAll(m);
        }

    put方法:

     public V put(K key, V value) {
        //如果为空,用空代替对象 Object k
    = maskNull(key);
    //计算hash指
    int h = hash(k);
    //获取桶 Entry
    <K,V>[] tab = getTable();
    //计算在桶的位置
    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)
    如果插入元素数量达到了句扩容至hash桶的两倍大小 resize(tab.length
    * 2); return null; }

    (1)计算hash;

    这里与HashMap有所不同,HashMap中如果key为空直接返回0,这里是用空对象来计算的。

    另外打散方式也不同,HashMap只用了一次异或,这里用了四次,HashMap给出的解释是一次够了,而且就算冲突了也会转换成红黑树,对效率没什么影响。

    (2)计算在哪个桶中;

    (3)遍历桶对应的链表;

    (4)如果找到元素就用新值替换旧值,并返回旧值;

    (5)如果没找到就在链表头部插入新元素;

    HashMap就插入到链表尾部。

    (6)如果元素数量达到了扩容门槛,就把容量扩大到2倍大小;

    HashMap中是大于threshold才扩容,这里等于threshold就开始扩容了。

    扩容:

     void resize(int newCapacity) {
            Entry<K,V>[] oldTable = getTable();
            int oldCapacity = oldTable.length;
            if (oldCapacity == MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return;
            }
    
            Entry<K,V>[] newTable = newTable(newCapacity);
            transfer(oldTable, newTable);
            table = newTable;
    
            if (size >= threshold / 2) {
                threshold = (int)(newCapacity * loadFactor);
            } else {
                expungeStaleEntries();
                transfer(newTable, oldTable);
                table = oldTable;
            }
        }
    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);

    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;
    // Must not null out e.next;
    // stale entries may be in use by a HashIterator
    e.value = null; // Help GC
    size--;
    break;
    }
    prev = p;
    p = next;
    }
    }
    }
    }
      private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
            for (int j = 0; j < src.length; ++j) {
                Entry<K,V> e = src[j];
                src[j] = null;
                while (e != null) {
                    Entry<K,V> next = e.next;
                    Object key = e.get();
                    if (key == null) {
                        e.next = null;  // Help GC
                        e.value = null; //  "   "
                        size--;
                    } else {
                        int i = indexFor(e.hash, dest.length);
                        e.next = dest[i];
                        dest[i] = e;
                    }
                    e = next;
                }
            }
        }

    (1)判断旧容量是否达到最大容量;

    (2)新建新桶并把元素全部转移到新桶中;

    (3)如果转移后元素个数不到扩容门槛的一半,则把元素再转移回旧桶,继续使用旧桶,说明不需要扩容;

    (4)否则使用新桶,并计算新的扩容门槛;

    (5)转移元素的过程中会把key为null的元素清除掉,所以size会变小;

    expungeStaleEntries

    ()1)当key失效的时候gc会自动把对应的Entry添加到这个引用队列中;

    (2)所有对map的操作都会直接或间接地调用到这个方法先移除失效的Entry,比如getTable()、size()、resize();

    (3)这个方法的目的就是遍历引用队列,并把其中保存的Entry从map中移除掉,具体的过程请看类注释;

    (4)从这里可以看到移除Entry的同时把value也一并置为null帮助gc清理元素,防御性编程。

    总结:

    (1)WeakHashMap使用(数组 + 链表)存储结构,并不是像hashmap一样,有红黑树的加入;

    (2)WeakHashMap中的key是弱引用,gc的时候会被清除;

    (3)每次对map的操作都会剔除失效key对应的Entry;

    (4)使用String作为key时,一定要使用new String()这样的方式声明key,才会失效,其它的基本类型的包装类型是一样的;

  • 相关阅读:
    mysql select 练习题
    mysql常用函数
    创建后表的修改:添加列+修改列+删除列+重命名表+删除整张表+删除整个数据库
    数据库里any 和 all 的区别
    mysql 高级查询
    9.12/ css3拓展、js基础语法、程序基本知识、数据类型、运算符表达方式、语句知识点
    9.11知识大纲【列表拓展】【格式与布局】
    9.10
    9.9
    9.8
  • 原文地址:https://www.cnblogs.com/xiufengchen/p/10722873.html
Copyright © 2011-2022 走看看