Java集合—WeakHashMap
WeakHashMap的使用
WeakHashMap的使用和HashMap类似,区别是WeakHashMap除了自身有对key的引用,没有其他引用,也就是弱引用,所以WeakHashMap中的key可能就会被GC(垃圾回收器)回收,value值也会被回收
WeakHashMap的特点
允许key和value值为null(和HashMap一样)
WeakHashMap也是哈希表,但它的key为弱键
底层数据结构为数组+链表
WeakHashMap底层源码实现
- 类的继承关系
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {
WeakHashMap和HashMap一样,继承自AbstractMap,实现Map接口
- 类的成员变量
//Entry[]数组类型,存储哈希表的键值对
Entry<K,V>[] table;
//已保存的键值对的数量
private int size;
//用来判断是否进行扩容(threshold>=size时进行扩容)
private int threshold;
//加载因子
private final float loadFactor;
//保存已被GC回收的key
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
//用来实现fast-fail机制
int modCount;
- 类的构造函数
1.默认构造函数
public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
2.自定义容量大小的构造函数
public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
3.自定义容量大小和加载因子的构造函数
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;
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
}
4.包含子Map的构造函数
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);
}
- 主要方法分析
1.put(K key, V value)方法
public V put(K key, V value) {
// 确定key值,允许key为null
Object k = maskNull(key);
// 获取器hash值
int h = hash(k);
// 获取tab
Entry<K,V>[] tab = getTable();
// 确定在tab中的位置 简单的&操作
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++;
// 取出i上的元素
Entry<K,V> e = tab[i];
// 构建链表,新元素在链表头
tab[i] = new Entry<>(k, value, queue, h, e);
// 检查是否需要扩容
if (++size >= threshold)
resize(tab.length * 2);
return null;
WeakHashMap的put操作与HashMap相似,都会进行覆盖操作,但是注意插入新节点是从链表头插入
2.get(Object key) 方法
public V get(Object key) {
// 确定key
Object k = maskNull(key);
// 计算其hashCode
int h = hash(k);
Entry<K,V>[] tab = getTable();
int index = indexFor(h, tab.length);
// 获取对应位置上的元素
Entry<K,V> e = tab[index];
while (e != null) {
// 如果hashCode相同,并且key也相同,则返回,否则继续循环
if (e.hash == h && eq(k, e.get()))
return e.value;
e = e.next;
}
// 未找到,则返回null
return null;
}
根据key找到对应元素,找到的返回该元素,找不到返回null
3.remove(Object key)方法
public V remove(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int i = indexFor(h, tab.length);
// 数组上第一个元素
Entry<K,V> prev = tab[i];
Entry<K,V> e = prev;
// 循环
while (e != null) {
Entry<K,V> next = e.next;
// 如果hash值相同,并且key一样,则进行移除操作
if (h == e.hash && eq(k, e.get())) {
// 修改次数自增
modCount++;
// 元素个数自减
size--;
// 如果就是头元素,则直接移除即可
if (prev == e)
tab[i] = next;
else
// 否则将前驱元素的next赋值为next,则将e移除
prev.next = next;
return e.value;
}
// 更新prev和e,继续循环
prev = e;
e = next;
}
return null;
}
在删除元素时,注意元素是链表头时直接移除元素即可
4.resize(int newCapacity)方法
WeakHashMap的扩容方法
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;
//扩容
if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
//清除被GC回收的value
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}
WeakHashMap的扩容要注意key可能会被GC回收的情况