Java
Java集合

Java集合15 - WeakHashMap详细介绍

简介:集合是Java中非常重要而且基础的内容,因为任何数据必不可少的就是该数据是如何存储的,集合的作用就是以一定的方式组织、存储数据。

1. WeakHashMap简介

WeakHashMap继承于AbstractMap,实现了Map接口。和HashMap一样,WeakHashMap也是一个散列表,它存储的内容也是键值对映射,而且键和值都可以是null。不过WeakHashMap的键是弱键。在WeakHashMap中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。

这个弱键是通过WeakReference和ReferenceQueue实现的。WeakHashMap的key是弱键,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的弱键。实现步骤是:

  1. 新建WeakHashMap,将键值对添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表节点,即多个Entry组成了一个键值对链表。
  2. 当某弱键不再被其它对象引用,并被GC回收时。在GC回收该弱键时,这个弱键也同时会被添加到ReferenceQueue(queue)队列中。
  3. 当下一次我们需要操作WeakHashMap时,会先同步tablequeuetable中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。这就是弱键如何被自动从WeakHashMap中删除的步骤了。

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

1.1. WeakHashMap的构造函数

  • // 默认构造函数
  • WeakHashMap()
  • // 指定容量大小的构造函数
  • WeakHashMap(int capacity)
  • // 指定容量大小和加载因子的构造函数
  • WeakHashMap(int capacity, float loadFactor)
  • // 包含子Map的构造函数
  • WeakHashMap(Map<? extends K, ? extends V> map)

1.2. WeakHashMap的API

  • void clear()
  • Object clone()
  • boolean containsKey(Object key)
  • boolean containsValue(Object value)
  • Set<Entry<K, V>> entrySet()
  • V get(Object key)
  • boolean isEmpty()
  • Set<K> keySet()
  • V put(K key, V value)
  • void putAll(Map<? extends K, ? extends V> map)
  • V remove(Object key)
  • int size()
  • Collection<V> values()

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

WeakHashMap与Map关系如下图:

1.WeakHashMap与Map关系.png

从图中可以看出:

  1. WeakHashMap继承于AbstractMap,并且实现了Map接口。
  2. WeakHashMap是哈希表,但是它的键是弱键。WeakHashMap中保护几个重要的成员变量:tablesizethresholdloadFactormodCountqueue
    • table:一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的键值对都是存储在Entry数组中的。
    • size:Hashtable的大小,它是Hashtable保存的键值对的数量。
    • threshold:Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold = size * loadFactor
    • loadFactor:加载因子。
    • modCount:用来实现fail-fast机制的
    • queue:保存的是已被GC清除的弱引用的键。

2. WeakHashMap源码解析

下面是WeakHashMap的源码,基于JDK1.6.0#_45:

TreeMap源码,点击菜单展开
  • 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本质上是一个单向链表
  • */
  • private Entry[] table;
  • // WeakHashMap的大小,它是WeakHashMap保存的键值对的数量
  • private int size;
  • // WeakHashMap的阈值,用于判断是否需要调整WeakHashMap的容量(threshold = size * loadFactor)
  • private int threshold;
  • // 加载因子实际大小
  • private final float loadFactor;
  • /**
  • * queue保存的是已被GC清除的弱引用的键
  • * 弱引用和ReferenceQueue 是联合使用的:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中
  • */
  • private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
  • // WeakHashMap被改变的次数
  • private volatile 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 = new Entry[capacity];
  • // 设置加载因子
  • this.loadFactor = loadFactor;
  • // 设置WeakHashMap阈值,当WeakHashMap中存储数据的数量达到threshold时,就需要将WeakHashMap的容量加倍
  • threshold = (int)(capacity * loadFactor);
  • }
  • // 指定容量大小的构造函数
  • public WeakHashMap(int initialCapacity) {
  • this(initialCapacity, DEFAULT#_LOAD#_FACTOR);
  • }
  • // 默认构造函数
  • public WeakHashMap() {
  • this.loadFactor = DEFAULT#_LOAD#_FACTOR;
  • threshold = (int)(DEFAULT#_INITIAL#_CAPACITY);
  • table = new Entry[DEFAULT#_INITIAL#_CAPACITY];
  • }
  • // 包含子Map的构造函数
  • public WeakHashMap(Map<? extends K, ? extends V> m) {
  • this(Math.max((int) (m.size() / DEFAULT#_LOAD#_FACTOR) + 1, 16), 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的特殊处理
  • private static <K> K unmaskNull(Object key) {
  • return (K) (key == NULL#_KEY ? null : key);
  • }
  • // 判断x和y是否相等
  • static boolean eq(Object x, Object y) {
  • return x == y || x.