Java 集合系列13之 WeakHashMap详细介绍(源码解析)和使用示例

233 阅读5分钟

第1部分 WeakHashMap介绍 WeakHashMap简介

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。

WeakHashMap的构造函数

WeakHashMap共有4个构造函数,如下:

复制代码 // 默认构造函数。 WeakHashMap()

// 指定“容量大小”的构造函数 WeakHashMap(int capacity)

// 指定“容量大小”和“加载因子”的构造函数 WeakHashMap(int capacity, float loadFactor)

// 包含“子Map”的构造函数 WeakHashMap(Map<? extends K, ? extends V> map) 复制代码

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 keySet() V put(K key, V value) void putAll(Map<? extends K, ? extends V> map) V remove(Object key) int size() Collection values() 复制代码

第2部分 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> {} 复制代码

WeakHashMap与Map关系如下图:

从图中可以看出: (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清除”的“弱引用的键”。

第3部分 WeakHashMap源码解析(基于JDK1.6.0_45) 下面对WeakHashMap的源码进行说明

View Code 说明:WeakHashMap和HashMap都是通过"拉链法"实现的散列表。它们的源码绝大部分内容都一样,这里就只是对它们不同的部分就是说明。

WeakReference是“弱键”实现的哈希表。它这个“弱键”的目的就是:实现对“键值对”的动态回收。当“弱键”不再被使用到时,GC会回收它,WeakReference也会将“弱键”对应的键值对删除。
“弱键”是一个“弱引用(WeakReference)”,在Java中,WeakReference和ReferenceQueue 是联合使用的。在WeakHashMap中亦是如此:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 接着,WeakHashMap会根据“引用队列”,来删除“WeakHashMap中已被GC回收的‘弱键’对应的键值对”。
另外,理解上面思想的重点是通过 expungeStaleEntries() 函数去理解。

第4部分 WeakHashMap遍历方式 4.1 遍历WeakHashMap的键值对

第一步:根据entrySet()获取WeakHashMap的“键值对”的Set集合。 第二步:通过Iterator迭代器遍历“第一步”得到的集合。

复制代码 // 假设map是WeakHashMap对象 // map中的key是String类型,value是Integer类型 Integer integ = null; Iterator iter = map.entrySet().iterator(); while(iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); // 获取key key = (String)entry.getKey(); // 获取value integ = (Integer)entry.getValue(); } 复制代码

4.2 遍历WeakHashMap的键

第一步:根据keySet()获取WeakHashMap的“键”的Set集合。 第二步:通过Iterator迭代器遍历“第一步”得到的集合。

复制代码 // 假设map是WeakHashMap对象 // map中的key是String类型,value是Integer类型 String key = null; Integer integ = null; Iterator iter = map.keySet().iterator(); while (iter.hasNext()) { // 获取key key = (String)iter.next(); // 根据key,获取value integ = (Integer)map.get(key); } 复制代码

4.3 遍历WeakHashMap的值

第一步:根据value()获取WeakHashMap的“值”的集合。 第二步:通过Iterator迭代器遍历“第一步”得到的集合。

复制代码 // 假设map是WeakHashMap对象 // map中的key是String类型,value是Integer类型 Integer value = null; Collection c = map.values(); Iterator iter= c.iterator(); while (iter.hasNext()) { value = (Integer)iter.next(); } 复制代码 WeakHashMap遍历测试程序如下:

View Code

第5部分 WeakHashMap示例 下面通过实例来学习如何使用WeakHashMap

View Code 运行结果:

复制代码 wmap:{three=w3, one=w1, two=w2} contains key two : true contains key five : false contains value 0 : false wmap: {one=w1, two=w2} next : two - w2 after gc WeakHashMap size:1 复制代码