Java集合框架之WeakHashMap详解

 更新时间:2023年09月07日 09:27:38   作者:∝ 1 颗心╮  
这篇文章主要介绍了Java集合框架之WeakHashMap详解,在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除,更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,需要的朋友可以参考下

WeakHashMap

WeakHashMap和HashMap相似,也是哈希表的实现,以键值对的形式存储数据,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。

部分顶部注释

Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.

WeakHashMap是Map接口的基于弱键的哈希表实现。当一个键不再正常使用,键对应的键值对将自动从WeakHashMap中删除。更严谨的说法是,键对应的键值对的存在并不阻止key被垃圾回收期回收,这就使该键称为可被终止的,最终被终止,被回收。当某个键被回收,它对应的键值对也就被从map中有效地删除了。所以WeakHashMap类表现地有些和其他的Map接口实现不同。

Both null values and the null key are supported. This class has performance characteristics similar to those of the HashMap class, and has the same efficiency parameters of initial capacity and load factor.

WeakHashMap特性与HashMap相似,WeakHashMap同样支持key和value为null,也有初始化容量和负载因子等参数。

Like most collection classes, this class is not synchronized. A synchronized WeakHashMap may be constructed using the Collections.synchronizedMap method.

像大多的集合类一样,WeakHashMap是非同步的。可以使用Collections.synchronizedMap来构造同步的WeakHashMap。

下面还有很多,不翻译了。

从以上的内容中我们可以总结出WeakHashMap的特点:

  • WeakHashMap特性与HashMap相似,也是哈希表的实现,以键值对的形式存储数据,同样支持key和value为null,也有初始化容量和负载因子等参数,也是非同步的。
  • WeakHashMap的键为“弱键”。

WeakHashMap数据结构

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> {}

(01) WeakHashMap继承于AbstractMap,并且实现了Map接口。

(02) WeakHashMap是哈希表,但是它的键是"弱键"。

WeakHashMap中保护几个重要的成员变量:table,size,threshold,loadFactor,modCount,queue。   

  • table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。   
  • size是Hashtable的大小,它是Hashtable保存的键值对的数量。   
  • threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。   
  • loadFactor就是加载因子。   
  • modCount是用来实现fail-fast机制的   
  • queue保存的是“已被GC清除”的“弱引用的键”。

源码分析

package java.util;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V> {
    // 默认的初始容量是16,必须是2的幂。
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
    private static final int MAXIMUM_CAPACITY = 1 << 30;
    // 默认加载因子
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    // 存储数据的Entry数组,长度是2的幂。
    // WeakHashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表
    Entry<K,V>[] table;
    // WeakHashMap的大小,它是WeakHashMap保存的键值对的数量
    private int size;
    // WeakHashMap的阈值,用于判断是否需要调整WeakHashMap的容量(threshold = 容量*加载因子)
    private int threshold;
    // 加载因子实际大小
    private final float loadFactor;
    // queue保存的是“已被GC清除”的“弱引用的键”。
    // 弱引用和ReferenceQueue 是联合使用的:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    // WeakHashMap被改变的次数
    int modCount;
    // 指定“容量大小”和“加载因子”的构造函数
    public WeakHashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                               initialCapacity);
        // WeakHashMap的最大容量只能是MAXIMUM_CAPACITY
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load factor: "+
                                               loadFactor);
        // 找出“大于initialCapacity”的最小的2的幂
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        // 创建Entry数组,用来保存数据
        table = newTable(capacity);
        // 设置“加载因子”
        this.loadFactor = loadFactor;
        // 设置“WeakHashMap阈值”,当WeakHashMap中存储数据的数量达到threshold时,就需要将WeakHashMap的容量加倍。
        threshold = (int)(capacity * loadFactor);
    }
    // 指定“容量大小”的构造函数
    public WeakHashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    // 默认构造函数。
    public WeakHashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }
    // 包含“子Map”的构造函数
    public WeakHashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                DEFAULT_INITIAL_CAPACITY),
             DEFAULT_LOAD_FACTOR);
        // 将m中的全部元素逐个添加到WeakHashMap中
        putAll(m);
    }
    // 键为null的mask值。
    // 因为WeakReference中允许“null的key”,若直接插入“null的key”,将其当作弱引用时,会被删除。
    // 因此,这里对于“key为null”的清空,都统一替换为“key为NULL_KEY”,“NULL_KEY”是“静态的final常量”。
    private static final Object NULL_KEY = new Object();
    // 对“null的key”进行特殊处理
    private static Object maskNull(Object key) {
        return (key == null) ? NULL_KEY : key;
    }
    // 还原对“null的key”的特殊处理
    static Object unmaskNull(Object key) {
        return (key == NULL_KEY) ? null : key;
    }
    // 判断“x”和“y”是否相等
    private static boolean eq(Object x, Object y) {
        return x == y || x.equals(y);
    }
    // 计算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);
    }
    // 返回哈希值对应的index
    // h & (length-1)保证返回值的小于length
    private static int indexFor(int h, int length) {
        return h & (length-1);
    }
    // 清空table中无用键值对。原理如下:
    // (01) 当WeakHashMap中某个“弱引用的key”由于没有再被引用而被GC收回时,
    //   被回收的“该弱引用key”也被会被添加到"ReferenceQueue(queue)"中。
    // (02) 当我们执行expungeStaleEntries时,
    //   就遍历"ReferenceQueue(queue)"中的所有key
    //   然后就在“WeakReference的table”中删除与“ReferenceQueue(queue)中key”对应的键值对
    private void expungeStaleEntries() {
        // 遍历队列
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    // 由此可以看出队列中的元素是Entry
                    Entry<K,V> e = (Entry<K,V>) x;
                // 获取entry对应桶的index
                int i = indexFor(e.hash, table.length);
                // 根据index获取table中对应的桶
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                // 如果桶不为null,遍历桶中节点,找到并删除与e相等的节点
                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; // 将e的value置为null
                        size--;// table大小减1
                        break;
                    }
                    // 准备遍历下个节点
                    prev = p;
                    p = next;
                }
            }
        }
    }
    // 获取WeakHashMap的table(存放键值对的数组)
    private Entry<K,V>[] getTable() {
        // 删除table中“已被GC回收的key对应的键值对”
        expungeStaleEntries();
        return table;
    }
    // 获取WeakHashMap的实际大小
    public int size() {
        if (size == 0)
            return 0;
        // 删除table中“已被GC回收的key对应的键值对”
        expungeStaleEntries();
        return size;
    }
    public boolean isEmpty() {
        return size() == 0;
    }
    // 获取key对应的value
    public V get(Object key) {
        Object k = maskNull(key);
        // 获取key的hash值。
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int index = indexFor(h, tab.length);
        Entry<K,V> e = tab[index];
        // 在“该hash值对应的链表”上查找“键值等于key”的元素
        while (e != null) {
            if (e.hash == h && eq(k, e.get()))
                return e.value;
            e = e.next;
        }
        return null;
    }
    // WeakHashMap是否包含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;
    }
    // 将“key-value”添加到WeakHashMap中
    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取代旧的value。然后退出!
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }
        // 若“该key”对应的键值对不存在于WeakHashMap中,则将“key-value”添加到table中
        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;
    }
    // 重新调整WeakHashMap的大小,newCapacity是调整后的单位
    void resize(int newCapacity) {
        Entry<K,V>[] oldTable = getTable();
        // 记录table大小
        int oldCapacity = oldTable.length;
        // 如果table大小为MAXIMUM_CAPACITY,就将threshold调整为Integer.MAX_VALUE,终止执行
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        // 新建一个newTable,将“旧的table”的全部元素添加到“新的newTable”中,
        // 然后,将“新的newTable”赋值给“旧的table”。
        Entry<K,V>[] newTable = newTable(newCapacity);
        transfer(oldTable, newTable);
        table = newTable;
        // 如果table大小大于等于threshold / 2
        if (size >= threshold / 2) {
            // 重新计算threshold
            threshold = (int)(newCapacity * loadFactor);
        } else {
            // 删除table中“已被GC回收的key对应的键值对”
            expungeStaleEntries();
            transfer(newTable, oldTable);
            table = oldTable;
        }
    }
    // 将src的所有键值对复制到dest中。
    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;
            }
        }
    }
    // 将"m"的全部元素都添加到WeakHashMap中
    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;
        // 计算容量是否足够,
        // 若“当前实际容量 < 需要的容量”,则将容量x2。
        if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }
        // 将“m”中的元素逐个添加到WeakHashMap中。
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }
    // 删除“键为key”元素
    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;
        // 删除链表中“键为key”的元素
        // 本质是“删除单向链表中的节点”
        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;
    }
    // 删除“键值对”
    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;
        // 删除链表中的“键值对e”
        // 本质是“删除单向链表中的节点”
        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;
    }
    // 清空WeakHashMap,将所有的元素设为null
    public void clear() {
        // clear out ref queue. We don't need to expunge entries
        // since table is getting cleared.
        while (queue.poll() != null)
            ;
        modCount++;
        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.
        while (queue.poll() != null)
            ;
    }
    // 是否包含“值为value”的元素
    public boolean containsValue(Object value) {
        // 若“value为null”,则调用containsNullValue()查找
        if (value==null)
            return containsNullValue();
        // 若“value不为null”,则查找WeakHashMap中是否有值为value的节点。
        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;
    }
    // 是否包含null值
    private boolean 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 (e.value==null)
                    return true;
        return false;
    }
    // Entry是单向链表。
    // 它是 “WeakHashMap链式存储法”对应的链表。
    // 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数
    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        // 指向下一个节点
        Entry<K,V> next;
        // 构造函数。
        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;
        }
        // 判断两个Entry是否相等
        // 若两个Entry的“key”和“value”都相等,则返回true。
        // 否则,返回false
        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;
        }
        // 实现hashCode()
        public int hashCode() {
            K k = getKey();
            V v = getValue();
            return Objects.hashCode(k) ^ Objects.hashCode(v);
        }
        public String toString() {
            return getKey() + "=" + getValue();
        }
    }
    // HashIterator是WeakHashMap迭代器的抽象出来的父类,实现了公共了函数。
    // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。
    private abstract class HashIterator<T> implements Iterator<T> {
        // 当前索引
        private int index;
        // 当前元素
        private Entry<K,V> entry;
        // 上一次返回元素
        private Entry<K,V> lastReturned;
        // expectedModCount用于实现fast-fail机制。
        private int expectedModCount = modCount;
        // 下一个键(强引用)
        private Object nextKey;
        // 当前键(强引用)
        private Object currentKey;
        // 构造函数
        HashIterator() {
            index = isEmpty() ? 0 : table.length;
        }
        // 是否存在下一个元素
        public boolean hasNext() {
            Entry<K,V>[] t = table;
            // 一个Entry就是一个单向链表
            // 若该Entry的下一个节点不为空,就将next指向下一个节点;
            // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。
            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;
        }
        // 获取下一个元素
        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;
        }
    }
    // value的迭代器
    private class ValueIterator extends HashIterator<V> {
        public V next() {
            return nextEntry().value;
        }
    }
    // key的迭代器
    private class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
    // Entry的迭代器
    private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }
    // WeakHashMap的Entry对应的集合
    private transient Set<Map.Entry<K,V>> entrySet;
    // 返回“key的集合”,实际上返回一个“KeySet对象”
    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();
            keySet = ks;
        }
        return ks;
    }
    // Key对应的集合
    // KeySet继承于AbstractSet,说明该集合中没有重复的Key。
    private class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return new KeyIterator();
        }
        public int size() {
            return WeakHashMap.this.size();
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            if (containsKey(o)) {
                WeakHashMap.this.remove(o);
                return true;
            }
            else
                return false;
        }
        public void clear() {
            WeakHashMap.this.clear();
        }
        public Spliterator<K> spliterator() {
            return new KeySpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
        }
    }
    // 返回“value集合”,实际上返回的是一个Values对象
    public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values();
            values = vs;
        }
        return vs;
    }
    // “value集合”
    // Values继承于AbstractCollection,不同于“KeySet继承于AbstractSet”,
    // Values中的元素能够重复。因为不同的key可以指向相同的value。
    private class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return new ValueIterator();
        }
        public int size() {
            return WeakHashMap.this.size();
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            WeakHashMap.this.clear();
        }
        public Spliterator<V> spliterator() {
            return new ValueSpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
        }
    }
    // 返回“WeakHashMap的Entry集合”
    // 它实际是返回一个EntrySet对象
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }
    // EntrySet对应的集合
    // EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。
    private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
        // 是否包含“值(o)”
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            Entry<K,V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        // 删除“值(o)”
        public boolean remove(Object o) {
            return removeMapping(o);
        }
        // 返回WeakHashMap的大小
        public int size() {
            return WeakHashMap.this.size();
        }
        // 清空WeakHashMap
        public void clear() {
            WeakHashMap.this.clear();
        }
        // 拷贝函数。将WeakHashMap中的全部元素都拷贝到List中
        private List<Map.Entry<K,V>> deepCopy() {
            List<Map.Entry<K,V>> list = new ArrayList<>(size());
            for (Map.Entry<K,V> e : this)
                list.add(new AbstractMap.SimpleEntry<>(e));
            return list;
        }
        // 返回Entry对应的Object[]数组
        public Object[] toArray() {
            return deepCopy().toArray();
        }
        // 返回Entry对应的T[]数组(T[]我们新建数组时,定义的数组类型)
        public <T> T[] toArray(T[] a) {
            return deepCopy().toArray(a);
        }
        public Spliterator<Map.Entry<K,V>> spliterator() {
            return new EntrySpliterator<>(WeakHashMap.this, 0, -1, 0, 0);
        }
    }
}

总结

WeakHashMap与HashMap比较

不同点

不同点HashMapWeakHashMap
数据结构数组+链表+红黑树数组+链表+队列
强引用弱引用
是否实现Cloneable和Serializable

相同点

  • 都是基于哈希表的实现。
  • 都以键值对的形式存储数据。
  • 都继承了AbstractMap,实现了Map接口。
  • 都支持key和value为null。
  • 都是非同步的。
  • 都是无序的。

什么是“弱键”?

先了解下什么是“弱引用”

弱引用, 在进行垃圾回收时,无论当前内存是否足够,都会回收掉只被弱引用关联着的对象,因此其生命周期只存在于一个垃圾回收周期内。

WeakHashMap的键就是弱引用。当某个键不再正常使用时,便自动移除其条目。

“弱键”会对WeakHashMap产生什么影响? 当一个键不再正常使用,键对应的键值对将自动从WeakHashMap中删除。键对应的键值对的存在并不阻止key被垃圾回收期回收,这就使该键称为可被终止的,最终被终止,被回收。当某个键被回收,它对应的键值对也就被从map中有效地删除了。所以WeakHashMap类表现地有些和其他的Map接口实现不同。

“弱键”是如何实现的?

WeakHashMap 中的Entry对象继承了 WeakReference,它把key封装成一个弱引用对象。

WeakHashMap .Entry

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;
    }
    ...
    ...
    ...

HashMap.Node

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    ...
    ...
    ...

对比从上面WeakHashMap和HashMap节点类的实现可以看出,WeakHashMap把key封装成一个弱引用对象。

想深入了解WeakHashMap的弱键,那么就必须先了解 ReferenceQueue 和 WeakReference。

到此这篇关于Java集合框架之WeakHashMap详解的文章就介绍到这了,更多相关Java的WeakHashMap内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JAVA实现双向链表的增删功能的方法

    JAVA实现双向链表的增删功能的方法

    本篇文章主要介绍了JAVA实现双向链表的增删功能的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • JavaWeb ServletContext基础与应用详细讲解

    JavaWeb ServletContext基础与应用详细讲解

    ServletConfig对象,叫Servlet配置对象。主要用于加载配置文件的初始化参数。我们知道一个Web应用里面可以有多个servlet,如果现在有一份数据需要传给所有的servlet使用,那么我们就可以使用ServletContext对象了
    2023-01-01
  • mybatis foreach 属性及其三种使用情况详解

    mybatis foreach 属性及其三种使用情况详解

    这篇文章主要介绍了mybatis foreach 属性及其三种使用情况详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • SpringBoot基于Minio实现分片上传、断点续传的实现

    SpringBoot基于Minio实现分片上传、断点续传的实现

    本文主要介绍了SpringBoot基于Minio实现分片上传、断点续传的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • 详解Java中的mapstruct插件使用

    详解Java中的mapstruct插件使用

    mapstruct 的插件是专门用来处理 domin 实体类与 model 类的属性映射的,我们只需定义 mapper 接口,mapstruct 在编译的时候就会自动的帮我们实现这个映射接口,避免了麻烦复杂的映射实现,对Java mapstruct使用相关知识感兴趣的朋友一起看看吧
    2022-04-04
  • java设计模式策略模式图文示例详解

    java设计模式策略模式图文示例详解

    这篇文章主要为大家介绍了java设计模式策略模式图文示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Java 设计模式中的策略模式详情

    Java 设计模式中的策略模式详情

    这篇文章主要介绍了Java 设计模式中的策略模式详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • 解决Callable的对象中,用@Autowired注入别的对象失败问题

    解决Callable的对象中,用@Autowired注入别的对象失败问题

    这篇文章主要介绍了解决Callable的对象中,用@Autowired注入别的对象失败问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 浅谈Spring中单例Bean是线程安全的吗

    浅谈Spring中单例Bean是线程安全的吗

    这篇文章主要介绍了浅谈Spring中单例Bean是线程安全的吗?具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • Eureka源码核心类预备知识

    Eureka源码核心类预备知识

    这篇文章主要为大家介绍了Eureka源码核心类预备知识详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10

最新评论