java源码解读--weakhashmap

WeakHashMap

WeakHasMap是针对弱引用的一个类,深入理解java虚拟机中对java引用概念进行了详细说明,在此不做重复解释。需要注意的是弱引用只存活到下一次垃圾回收之前,WeakHasMap中的键都是弱引用类型,所以该类可以自动移除已经被回收的键所对应的k-v对。

  • 该类支持null值输入
  • 默认每次扩容一倍
  • fail-fast

类定义

继承了AbstractMap类,实现了Map接口,注意该类没有实现cloneable、serializable接口

1
2
3
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {

类变量和成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//默认初始容量
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;
//referencequeue用于弱引用的回收
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
//修改次数
int modCount;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

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);
//大于initialCapacity最小的2的幂次
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
//初始化table
table = newTable(capacity);
//负载因子赋值
this.loadFactor = loadFactor;
//计算扩容阈值
threshold = (int)(capacity * loadFactor);
}

public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}


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);
}

entry

WeakHashMap中的entry继承了WeakReference,可以在gc时放入ReferenceQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;

/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
//这里把只关联了key,只有当key被回收时,entry才会被放入ReferenceQueue
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}

......

}

put

put函数会自动调用移除已释放引用相关方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

public V put(K key, V value) {
//null值统一使用NULL_KEY代替
Object k = maskNull(key);
//获取hash值
int h = hash(k);
//getTable方法会调用expungeStaleEntries()方法进行已释放引用的移除
Entry<K,V>[] tab = getTable();
//计算下标
int i = indexFor(h, tab.length);
//查找是否已经存储过该key
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++;
//table不存在该key,则新建entry并放入对应下标位置
Entry<K,V> e = tab[i];
tab[i] = new Entry<>(k, value, queue, h, e);
//如果容量不够,扩容一倍
if (++size >= threshold)
resize(tab.length * 2);
return null;
}

get

get方法也会自动调用清除已释放引用相关的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14

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;
}

expungeStaleEntries

该方法用于移除已释放元素,该类中很多方法会间接调用该方法,如size,getTable,resize,get,put

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

private void expungeStaleEntries() {
//已经释放的弱引用会加入ReferenceQueue中,所以遍历queue
for (Object x; (x = queue.poll()) != null; ) {
//同步操作
synchronized (queue) {
@SuppressWarnings("unchecked")
//从queue取出来的是entry,这点由entry定义决定
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;
}
}
}
}

resize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//需传入扩容后容量
void resize(int newCapacity) {
//此处会调用expungeStaleEntries
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 {
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}
-------------本文结束感谢您的阅读-------------

本文标题:java源码解读--weakhashmap

文章作者:小建儿

发布时间:2018年06月21日 - 18:06

最后更新:2018年06月21日 - 18:06

原始链接:http://yajian.github.io/java源码解读-weakhashmap/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。