WeakHashMap

1、简介

1.1 介绍
WeakHashMap 继承于AbstractMap,实现了Map接口。
和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。

不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:
(01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
(02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
(03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。

和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。

1.2 构造
//构造一个新的,空的 WeakHashMap ,默认的初始容量(16)和负载因子(0.75)。
WeakHashMap()
//构造一个新的,空的 WeakHashMap ,具有给定的初始容量和默认负载因子(0.75)。
WeakHashMap(int initialCapacity)
//构造一个新的,空的 WeakHashMap与给定的初始容量和给定的负载因子。
WeakHashMap(int initialCapacity, float loadFactor)
//构造一个新的 WeakHashMap与指定的地图相同的映射。
WeakHashMap(Map<? extends K,? extends V> m)
1.3 常用方法
void    clear()
从这张地图中删除所有的映射。
boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回 trueboolean containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回 trueSet<Map.Entry<K,V>> entrySet()
返回此地图中包含的映射的Set视图。
void    forEach(BiConsumer<? super K,? super V> action)
对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。
V   get(Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()
如果此地图不包含键值映射,则返回 trueSet<K>  keySet()
返回此地图中包含的键的Set视图。
V   put(K key, V value)
将指定的值与此映射中的指定键相关联。
void    putAll(Map<? extends K,? extends V> m)
将指定地图的所有映射复制到此地图。
V   remove(Object key)
如果存在,则从此弱散列映射中删除密钥的映射。
void    replaceAll(BiFunction<? super K,? super V,? extends V> function)
将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。
int size()
返回此地图中键值映射的数量。
Collection<V>   values()
返回此地图中包含的值的Collection视图。
1.4 继承关系
java.lang.Object
   ↳     java.util.AbstractMap<K, V>
         ↳     java.util.WeakHashMap<K, V>

public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V> {}
1.5 类图

这里写图片描述

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 threshold;
    //清除元素的引用队列
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    //新建节点数组
    private Entry<K,V>[] newTable(int n) {
        return (Entry<K,V>[]) new Entry<?,?>[n];
    }
    //带初始长度和加载因子的构造
    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;
        //大于initialCapacity的2的幂的数
        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);
    }
    //key为null,value会被直接回收,所以使用Object代替null
    private static final Object NULL_KEY = new Object();
    //扰动函数,使key分布更均匀
    final int hash(Object k) {
        int h = k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
    //根据key计算桶位置
    private static int indexFor(int h, int length) {
        return h & (length-1);
    }
    //从table中删除被垃圾回收的节点
    //Entry的key关联ReferenceQueue,key被回收后将放入queue
    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;
                }
            }
        }
    }
    //返回table
    private Entry<K,V>[] getTable() {
        //删除被回收的元素
        expungeStaleEntries();
        return table;
    }
    //根据key获取value
    public V get(Object key) {
        //构建key
        Object k = maskNull(key);
        //计算key的hash值
        int h = hash(k);
        //获取最新的table
        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;
    }
    //是否包含key
    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }
    //根据key获取节点
    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();
        int i = indexFor(h, tab.length);

        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            //如果key相同 替换value
            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);
        //如果元素个数大于临界值 扩展为原容量*2
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }
    //扩展
    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 ignoring null elements and processing ref queue caused massive
         * shrinkage, then restore old table.  This should be rare, but avoids
         * unbounded expansion of garbage-filled tables.
         */
         //如果垃圾回收导致元素个数不及threshold/2,则将元素还原为就table,防止占用过多的内存
        if (size >= threshold / 2) {
            threshold = (int)(newCapacity * loadFactor);
        } else {
            expungeStaleEntries();
            transfer(newTable, oldTable);
            table = oldTable;
        }
    }
    //转移元素
    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;
            }
        }
    }
    //移除元素
    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;
            //如果key相同 则删除
            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;
    }
    //移除一个元素
    boolean removeMapping(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Entry<K,V>[] tab = getTable();
        Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
        Object k = maskNull(entry.getKey());
        int h = hash(k);
        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 && e.equals(entry)) {
                modCount++;
                size--;
                if (prev == e)
                    tab[i] = next;
                else
                    prev.next = next;
                return true;
            }
            prev = e;
            e = next;
        }

        return false;
    }
    //清除所有元素
    public void clear() {
        // clear out ref queue. We don't need to expunge entries
        // since table is getting cleared.
        //弹出queue的所有数据
        while (queue.poll() != null)
            ;

        modCount++;
        //填充为null
        Arrays.fill(table, null);
        size = 0;

        // Allocation of array may have caused GC, which may have caused
        // additional entries to go stale.  Removing these entries from the
        // reference queue will make them eligible for reclamation.
        //将table填充为null后,会有一波回收
        while (queue.poll() != null)
            ;
    }
    //是否包含value 将进行全表查找 很耗时
    public boolean containsValue(Object value) {
        if (value==null)
            return containsNullValue();

        Entry<K,V>[] tab = getTable();
        for (int i = tab.length; i-- > 0;)
            for (Entry<K,V> e = tab[i]; e != null; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }
    //节点类 继承自WeakReference
    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.
         */
         //构造将传进一个ReferenceQueue对象
        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();
        }
    }
    //迭代器
    private abstract class HashIterator<T> implements Iterator<T> {
        private int index;
        private Entry<K,V> entry;
        private Entry<K,V> lastReturned;
        private int expectedModCount = modCount;

        /**
         * Strong reference needed to avoid disappearance of key
         * between hasNext and next
         */
        private Object nextKey;

        /**
         * Strong reference needed to avoid disappearance of key
         * between nextEntry() and any use of the entry
         */
        private Object currentKey;

        HashIterator() {
            index = isEmpty() ? 0 : table.length;
        }

        public boolean hasNext() {
            Entry<K,V>[] t = table;

            while (nextKey == null) {
                Entry<K,V> e = entry;
                int i = index;
                while (e == null && i > 0)
                    e = t[--i];
                entry = e;
                index = i;
                if (e == null) {
                    currentKey = null;
                    return false;
                }
                nextKey = e.get(); // hold on to key in strong ref
                if (nextKey == null)
                    entry = entry.next;
            }
            return true;
        }

        /** The common parts of next() across different types of iterators */
        protected Entry<K,V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (nextKey == null && !hasNext())
                throw new NoSuchElementException();

            lastReturned = entry;
            entry = entry.next;
            currentKey = nextKey;
            nextKey = null;
            return lastReturned;
        }

        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

            WeakHashMap.this.remove(currentKey);
            expectedModCount = modCount;
            lastReturned = null;
            currentKey = null;
        }

    }
    //key集
    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();
            keySet = ks;
        }
        return ks;
    }
    //value集
    public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values();
            values = vs;
        }
        return vs;
    }
    //Entry集
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }
}
2.2 遍历
public static void main(String[] args) {
        WeakHashMap map = new WeakHashMap();
        Object key, value;
        //元素集  推荐
        Iterator iter = map.entrySet().iterator();
        while(iter.hasNext()) {
            Map.Entry entry = (Map.Entry)iter.next();
            key = (String)entry.getKey();
            value = (Integer)entry.getValue();
        }
        //key集
        Iterator iterKey = map.keySet().iterator();
        while (iterKey.hasNext()) {
            key = (String)iterKey.next();
            // 根据key,获取value
            value = (Integer)map.get(key);
        }
        //value集
        Collection c = map.values();
        Iterator iterVal= c.iterator();
        while (iter.hasNext()) {
            value = iter.next();
        }
    }
2.3 总结

WeakHashMap与jdk1.7的hashmap类似,长度都为2的次方数组加链表形式

WeakHashMap的Entry继承自WeakReference且key是弱引用,并关联一个ReferenceQueue。因为key是弱引用,jvm进行一次gc时,会回收key,并将Entry放入ReferenceQueue。queue.poll()弹出Entry,WeakHashMap从table中找到弹出的Entry并删除。此时这个Entry将被彻底回收。(Entry继承自WeakReference条件很重要,删除主要在expungeStaleEntries())

WeakHashMap的key可以为null

参考:
http://www.cnblogs.com/skywang12345/p/3311092.html
http://blog.csdn.net/kjfcpua/article/details/8495199
http://blog.csdn.net/u012332679/article/details/57489179

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值