WeakHashMap里面的键是弱键,也就是和弱引用相关的。
如果put到WeakHashMap里面的键值被垃圾回收了,此键值就会放到WeakHashMap的ReferenceQueue中,那么在下次操作WeakHashMap时候,会去拿ReferenceQueue里面的元素和Map里存的key比较,如果相同,就把Map对应的元素删除(因为被垃圾回收也就是没有任何引用到它的对象了)
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {
// 成员变量和HashMap相似
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final int MAXIMUM_CAPACITY = 1 << 30;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
Entry<K,V>[] table;
private int size;
private int threshold;
private final float loadFactor;
/**
* 弱引用队列,如果key被垃圾回收器回收了,那么key就会放到这个队列里面来
* 到时候把这个队列里面的key与WeakHashMap里面的比较并删除
*/
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
// 保证fast-fail
int modCount;
}
// put()操作没什么特别之处,数据结构是数组+链表
public V put(K key, V value) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int i = indexFor(h, tab.length);
for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
if (h == e.hash && eq(k, e.get())) {
V oldValue = e.value;
if (value != oldValue)
e.value = value;
return oldValue;
}
}
modCount++;
Entry<K,V> e = tab[i];
// 新建一个节点
tab[i] = new Entry<>(k, value, queue, h, e);
// 判断是否需要扩容
if (++size >= threshold)
resize(tab.length * 2);
return null;
}
// 看下Entry的构造函数,看看key和queue怎么关联的
// Entry继承了WeakReference,说明Entry本身就是一个弱引用对象
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
// 调用WeakReference的构造函数
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的构造函数
super(referent, q);
}
}
public abstract class Reference<T> {
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
@SuppressWarnings("rawtypes")
Reference next;
/* When active: next element in a discovered reference list maintained by GC (or this if last)
* pending: next element in the pending list (or null if last)
* otherwise: NULL
*/
transient private Reference<T> discovered; /* used by VM */
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
}
// 扩容函数
void resize(int newCapacity) {
// getTable后,可能size已经比原来的小很多了,因为getTable里面调用了清理key的函数
Entry<K,V>[] oldTable = getTable();
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry<K,V>[] newTable = newTable(newCapacity);
// 转换过程中,遍历到key==null时,size也会变小,并且置了一些entry为null
transfer(oldTable, newTable);
table = newTable;
/*
* If ignoring null elements and processing ref queue caused massive
* shrinkage, then restore old table. This should be rare, but avoids
* unbounded expansion of garbage-filled tables.
*/
if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
// 存储的元素比阀值的一半还小
// 先清理一下被回收的key
expungeStaleEntries();
// 把newtable转换到oldtable,因为oldtable的容量还够
transfer(newTable, oldTable);
table = oldTable;
}
}
// 返回table之前,先清理一下被回收的key。大部分操作都调用了这个函数
private Entry<K,V>[] getTable() {
expungeStaleEntries();
return table;
}
// 把table与queue里面的key比较,如果有相同的,则除去table里面的那个entry
// 因为queue里面存的是已经被GC的key了
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
里面的大部分函数都调用了getTable(),也就是调用了expungeStaleEntries(),每次操作前都会清理一下无效的key