WeakHashMap
基于 动态数组和链表 实现的弱键映射
WeakHashMap继承关系
WeakHashMap继承了AbstractMap抽象类,拥有Map的基本操作
WeakHashMap源码解析
WeakHashMap构造函数:
/**
* 使用给定的初始容量和给定的负载因子构造一个新的空 WeakHashMap
*
* @param initialCapacity The initial capacity of the {@code WeakHashMap}
* @param loadFactor The load factor of the {@code WeakHashMap}
* @throws IllegalArgumentException if the initial capacity is negative,
* or if the load factor is nonpositive.
*/
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1; // 保证为2的幂
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor); // 阈值
}
/**
* 使用给定的初始容量和默认的负载因子(0.75)构造一个新的空 WeakHashMap
*
* @param initialCapacity The initial capacity of the {@code WeakHashMap}
* @throws IllegalArgumentException if the initial capacity is negative
*/
public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 使用默认的初始容量(16)和负载因子(0.75)构造一个新的空 WeakHashMap
*/
public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* 使用与指定映射相同的映射构造新的@code WeakHashMap
* 使用默认的负载因子(0.75)和足够容纳指定映射的初始容量
*
* @param m the map whose mappings are to be placed in this map
* @throws NullPointerException if the specified map is null
* @since 1.3
*/
public WeakHashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR);
putAll(m);
}
@SuppressWarnings("unchecked")
private Entry<K,V>[] newTable(int n) {
return (Entry<K,V>[]) new Entry<?,?>[n];
}
/**
* 将指定映射的所有映射复制到此映射
* 这些映射将替换当前指定映射中任何键的映射
*
* @param m mappings to be stored in this map.
* @throws NullPointerException if the specified map is null.
*/
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size(); // 需添加映射的数量
if (numKeysToBeAdded == 0)
return;
/*
* 如果要添加的映射的数量>=阈值, 则展开映射
* 若(m.size() + size) >= threshold, 但是如果要添加的键与此映射中已经存在的键重叠, 则此条件可能导致映射的容量是适当容量的两倍
* 通过使用保守计算,我们使自己最多调整一次大小
*/
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
WeakHashMap删除过时条目函数:
/**
* 从表中删除过时的条目
*/
private void expungeStaleEntries() {
// queue:引用队列, 记录将被清除的弱条目
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x; // 待删除条目e
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)
// 表示e是桶链表的第一个条目
// 条目prev = 被删条目e, 则删除table的条目e
table[i] = next;
else
prev.next = next; // 跳过e
// 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; // 移动到下一条目, 继续比对
}
}
}
}
WeakHashMap的增删/查询操作:
/**
* 返回指定键映射到的值
* 若此映射不包含键的映射返回null
*
* @see #put(Object, Object)
*/
public V get(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int index = indexFor(h, tab.length);
Entry<K,V> e = tab[index];
while (e != null) {
if (e.hash == h && eq(k, e.get()))
return e.value;
e = e.next;
}
return null;
}
/**
* 将此映射的内容重新散列到具有更大容量的新数组中
* 当此映射中的键数达到其阈值时, 将自动调用此方法
*
* 如果当前容量为MAXIMUM_CAPACITY, 则此方法不调整映射的大小,
* 而是将threshold设置为Integer.MAX_VALUE, 这可以防止将来的调用
*
* @param newCapacity the new capacity, MUST be a power of two;
* must be greater than current capacity unless current
* capacity is MAXIMUM_CAPACITY (in which case value
* is irrelevant).
*/
void resize(int newCapacity) {
Entry<K,V>[] oldTable = getTable(); // 获取存储数组
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) { // 旧容量达到最大容量
threshold = Integer.MAX_VALUE;
return;
}
Entry<K,V>[] newTable = newTable(newCapacity); // 更新最大容量
transfer(oldTable, newTable); // 复制映射条目
table = newTable;
/*
* 如果忽略空元素并处理ref队列导致大量收缩, 则恢复旧表
* 这种情况应该很少见, 但是可以避免无限地扩展充满垃圾的表
*/
if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
expungeStaleEntries(); // 清理不引用条目
transfer(newTable, oldTable); // 复制元素
table = oldTable;
}
}
/**
* 返回此映射中的键值映射的数目
* 此结果是一个快照, 可能不反映未处理的条目
* 这些条目将在下一次尝试访问之前被删除
*/
public int size() {
if (size == 0)
return 0;
expungeStaleEntries(); // 删除不引用的条目
return size;
}
/**
* 将指定值与此映射中的指定键关联
* 如果先前的映射包含此键的映射, 则将替换旧值
*
* @param key key with which the specified value is to be associated.
* @param value value to be associated with the specified key.
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}.
* (A {@code null} return can also indicate that the map
* previously associated {@code null} with {@code key}.)
*/
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;
}
/**
* 在首次删除过时的条目后返回表
*/
private Entry<K,V>[] getTable() {
expungeStaleEntries();
return table;
}
WeakHashMap总结
WeakHashMap基于 数组+链表 实现的存储结构
WeakHashMap是非线程安全的
WeakHashMap允许null元素
WeakHashMap使用弱键储存键值对,弱键不被引用会放入ReferenceQueue中,然后通过GC回收