欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。
简介
WeakHashMap是一种弱引用map,内部的key会存储为弱引用,当jvm gc的时候,如果这些key没有强引用存在的话,会被gc回收掉,下一次当我们操作map的时候会把对应的Entry整个删除掉,基于这种特性,WeakHashMap特别适用于缓存处理。
继承体系
可见,WeakHashMap没有实现Clone和Serializable接口,所以不具有克隆和序列化的特性。
存储结构
WeakHashMap因为gc的时候会把没有强引用的key回收掉,所以注定了它里面的元素不会太多,因此也就不需要像HashMap那样元素多的时候转化为红黑树来处理了。
因此,WeakHashMap的存储结构只有(数组 + 链表)。
源码解析
属性
/**
* 默认初始容量为16
*/
private static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* 最大容量为2的30次方
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认装载因子
*/
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 桶
*/
Entry<K,V>[] table;
/**
* 元素个数
*/
private int size;
/**
* 扩容门槛,等于capacity * loadFactor
*/
private int threshold;
/**
* 装载因子
*/
private final float loadFactor;
/**
* 引用队列,当弱键失效的时候会把Entry添加到这个队列中
*/
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
(1)容量
容量为数组的长度,亦即桶的个数,默认为16,最大为2的30次方,当容量达到64时才可以树化。
(2)装载因子
装载因子用来计算容量达到多少时才进行扩容,默认装载因子为0.75。
(3)引用队列
当弱键失效的时候会把Entry添加到这个队列中,当下次访问map的时候会把失效的Entry清除掉。
Entry内部类
WeakHashMap内部的存储节点, 没有key属性。
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
// 可以发现没有key, 因为key是作为弱引用存到Referen类中
V value;
final int hash;
Entry<K,V> next;
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
// 调用WeakReference的构造方法初始化key和引用队列
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
}
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent, ReferenceQueue<? super T> q) {
// 调用Reference的构造方法初始化key和引用队列
super(referent, q);
}
}
public abstract class Reference<T> {
// 实际存储key的地方
private T referent; /* Treated specially by GC */
// 引用队列
volatile ReferenceQueue<? super T> queue;
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
}
从Entry的构造方法我们知道,key和queue最终会传到到Reference的构造方法中,这里的key就是Reference的referent属性,它会被gc特殊对待,即当没有强引用存在时,当下一次gc的时候会被清除。
构造方法
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException