HashMap 是常用的数据结构,1.7是数组+链表,1.8是数组+红黑树,之前我写过AVL树的文章,过几天写一下红黑树的。
github:https://github.com/ai2101039/SoftHashMap
今天的主题是:给HashMap 增加软引用功能。
也许有人想,我直接 HashMap 的 V 使其继承软引用不就可以了?我想说,你说的对,只是不全面。
一、适用场景
V所对应的对象数量众多,可能会导致内存溢出。
二、非常重要
先看全代码,然后从上往下读代码,读完之后,看我最后的解释图,这样就清楚了。
三、直接上代码,代码内把重点地方进行标注解答
/**
* @author 高延荣
* @date 2018/12/10 11:40
* 描述: 具备软引用功能的 HashMap
*/
public class SoftHashMap<K, V> extends HashMap<K, V> {
/**
* queue,软引用标记队列
* <p>
* ★★★★★★★ 解释 ★★★★★★★
* 当SoftNode中 Value 被回收时,SoftNode 对象会被放入 queue中,以表示当前SoftNode 中的Value不存在
* 对我们的使用好处就是,我们读取 queue 队列,取出 SoftNode对象,取出其内部的 Key
* 以便于 temp 通过 key remove
*/
private ReferenceQueue<V> queue;
/**
* 真正的map对象
* 1、temp 内部 封装的 Node 强引用 K 和 SoftNode
* 2、SoftNode 内部强引用K,弱引用真正的Value
*/
private HashMap<K, SoftNode<K, V>> temp;
public SoftHashMap() {
queue = new ReferenceQueue<>();
temp = new HashMap<>();
}
@Override
public V get(Object key) {
clearQueue();
// 通过 key进行取值,如果为null,返回null,否则返回 SoftNode 软引用的值
SoftNode softNode = temp.get(key);
return softNode == null ? null : (V) softNode.get();
}
@Override
public V put(K key, V value) {
clearQueue();
// 创建 SoftNode对象
SoftNode softNode = new SoftNode(key, value, queue);
// 返回key之前所对应的SoftNode对象,即oldSoftNode
SoftNode oldSoftNode = temp.put(key, softNode);
// 如果oldSoftNode为null,就返回null,否则就返回 oldSoftNode所软引用的 Value
return oldSoftNode == null ? null : (V) oldSoftNode.get();
}
@Override
public boolean containsKey(Object key) {
clearQueue();
return temp.containsKey(key);
}
@Override
public V remove(Object key) {
clearQueue();
SoftNode<K, V> remove = temp.remove(key);
return remove == null ? null : remove.get();
}
@Override
public int size() {
clearQueue();
return temp.size();
}
/**
* 通过软引用队列内的 SoftNode,获取Key,然后temp 清除此 Key
* @see ReferenceQueue poll()
* poll() -- 类似于 stack 的pop(),移除并返回此对象
*/
private void clearQueue() {
SoftNode poll;
while ((poll = (SoftNode) queue.poll()) != null) {
temp.remove(poll.key);
}
}
/**
* 对V进行软引用的类
* @param <K> key,用于当 V 被回收后,temp 可以通过 key 进行移除
* @param <V> Value,真正的值
*
* 传入的queue,用于当Value被回收后,将 SoftNode对象放入 queue中,
* 以便于表示 某 SoftNode对象中的Value 已经被收回了。
*/
private class SoftNode<K, V> extends SoftReference<V> {
K key;
public SoftNode(K k, V v, ReferenceQueue queue) {
super(v, queue);
key = k;
}
}
}
我建议大家可以直接创建一个类去看。
四、画图解答
五、答疑
1、为什么SoftNode 需要传入 K,和 ReferenceQueue
A:
传入 queue 是为了当 V 被回收后,将 SoftNode对象传入到 queue中,以便于通知使用者,这个 SoftNode已经没有Value了
传入 K,是为了我们的temp 可以通过 K 进行移除无效数据
2、为什么要使用函数 clearQueue()
private void clearQueue() {
SoftNode poll;
while ((poll = (SoftNode) queue.poll()) != null) {
temp.remove(poll.key);
}
}
A:
举例,如果 从 K1 ~ K10 对应的 SoftNode 1 ~ SoftNode 10
SoftNode 1 ~ SoftNode 10 内部的 Value 均为 null,即均被回收了。
那么此时 temp 中的 K1 ~ K10 就应该被移除掉。
所以每次 put get 等函数 都会调用清除动作。
3、软引用到底啥意思
if(内存溢出){
v = null;
}
4、弱引用到底啥意思
if(gc){
v = null;
}