写在前面
在缓存场景下,由于内存是有限的,不能缓存所有对象,因此就须要一定的删除机制,淘汰掉一些对象。这个时候可能很快就想到了各种Cache数据逾期策略,目前也有一些优秀的包提供了功能丰富的Cache,比如Google的Guava Cache,它支持数据定期逾期、LRU、LFU等策略,但它仍然有可能会导致有用的数据被淘汰,没用的数据迟迟不淘汰(如果策略利用适当的情况下这都是小概率事件)。
现在有种机制,可以让Cache里不用的key数据自动清算掉,用的还留着,不会出现误删除。而WeakHashMap 就实用于这种缓存的场景,由于它有自清算机制!
如果让你手动实现一种自清算的HashMap,可以怎么做?首先肯定是想办法先知道某个Key肯定没有在用了,然后清算掉HashMap中没有在用的对应的K-V。在JVM里一个对象没用了是指没有任何其他有用对象直接或者间接实行它,详细点就是在GC过程中它是GCRoots不可达的。而某个弱引用对象所指向的对象如果被判定为垃圾对象,Jvm会将该弱引用对象放到一个ReferenceQueue里,只须要看下这个Queue里的内容就知道某个对象另有没有用了。
WeakHashMap概述
从WeakHashMap名字也可以知道,这是一个弱引用的Map,当进行GC回收时,弱引用指向的对象会被GC回收。
WeakHashMap正是由于利用的是弱引用,因此它的对象可能被随时回收。更直观的说,当利用 WeakHashMap 时,即使没有显示的添加或删除任何元素,也可能发生如下情况:
- 调用两次size()方法返回差别的值;
- 两次调用isEmpty()方法,第一次返回false,第二次返回true;
- 两次调用containsKey()方法,第一次返回true,第二次返回false,尽管两次利用的是同一个key;
- 两次调用get()方法,第一次返回一个value,第二次返回null,尽管两次利用的是同一个对象。
从上图可以看出:
- WeakHashMap继承于AbstractMap,并且实现了Map接口。
- 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清除”的“弱引用的键”。
基本用法
- WeakHashMap < String, String > weakHashMap = new WeakHashMap < > (10);
- String key0 = new String("str1");
- String key1 = new String("str2");
- String key2 = new String("str3");
- // 存放元素
- weakHashMap.put(key0, "data1");
- weakHashMap.put(key1, "data2");
- weakHashMap.put(key2, "data3");
- System.out.printf("weakHashMap: %s\n", weakHashMap);
- // 是否包含某key
- System.out.printf("contains key str1 : %s\n", weakHashMap.containsKey(key0));
- System.out.printf("contains key str2 : %s\n", weakHashMap.containsKey(key1));
- // 移除key
- weakHashMap.remove(key0);
- System.out.printf("weakHashMap after remove: %s\n", weakHashMap);
- // 这意味着"弱键"key1再没有被其它对象引用,调用gc时会回收WeakHashMap中与key1对应的键值对
- key1 = null;
- // 内存回收,这里会回收WeakHashMap中与"key0"对应的键值对
- System.gc();
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- // 遍历WeakHashMap
- for (Map.Entry < String, String > m: weakHashMap.entrySet()) {
- System.out.printf("next : %s >>> %s\n", m.getKey(), m.getValue());
- }
- // 打印WeakHashMap的实际大小
- System.out.printf("after gc WeakHashMap size: %s\n", weakHashMap.size());
复制代码 底层源码
构造器
[code]// 默认构造函数。WeakHashMap()// 指定“容量大小”的构造函数WeakHashMap(int capacity)// 指定“容量大小”和“加载因子”的构造函数WeakHashMap(int capacity, float loadFactor)// 包含“子Map”的构造函数WeakHashMap(Map |