基于弱引用键实现的HashMap,非线程安全。
1)当key所引用的对象只有弱引用时,在GC回收该对象后,会自动删除所关联的Entry;
2)其行为部分依赖GC;
3)value是强引用的。确保value没有直接或间接引用key,否则会阻止key引用对象的回收。间接引用:value1强引用key2的对象,其所关联的value2强引用key1的对象。
4)迭代器fail-fast。
与HashMap唯一的区别就是:WeakHashMap会将key封装到WeakReference,且关联ReferenceQueue,实现key的弱引用:
Entry[] table;
/**
* Reference queue for cleared WeakEntries
*/
private final ReferenceQueue
与HashMap的区别是:table、threshold会在容器中完成初始化,没有提供init()扩展接口;HashMap会在第一个键值对put时完成table初始化:
// 带初始容量、负载因子构造
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);
// table、loadFactor初始化
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
}
// 带初始容量构造
public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// 无参构造
public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
// 带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);
}
在增删改查的操作中,首先做的第一件事情就是调用getTable,从WeakHashMap中删除key引用的对象已回收的关联键值对:
/**
* Returns the table after first expunging stale entries.
*/
private Entry[] getTable() {
expungeStaleEntries();
return table;
}
// 从WeakHashMap中删除key引用的对象已回收的关联键值对
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry e = (Entry) x;
int i = indexFor(e.hash, table.length);
Entry prev = table[i];
Entry p = prev;
while (p != null) {
Entry 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
// 由于expungeStaleEntries的调用不会引起modCount的变化,
// HashIterator的遍历感知不到其操作,所以next不能null
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
与HashMap基本一样,只是在扩展过程中,需要考虑WeakHashMap的特性:
1)转移前首先从WeakHashMap中删除key引用的对象已回收的关联键值对,转移过程中已经回收的也不转移;
2)转移完成后,对于size低于threshold / 2的情况进行处理:容量不扩展,重新转移回来。
void resize(int newCapacity) {
Entry[] oldTable = getTable();// 从WeakHashMap中删除key引用的对象已回收的关联键值对:
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = newTable(newCapacity);
boolean oldAltHashing = useAltHashing;
useAltHashing |= sun.misc.VM.isBooted() &&
(newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean rehash = oldAltHashing ^ useAltHashing;
transfer(oldTable, newTable, rehash);
table = newTable;
// 如果因为getTable、transfer去掉的关联键值对比较多,
// size低于threshold / 2,则采用oldTable,将newTable中的键值对重新转移过来
// 这种情况比较少,但对于WeakHashMap来说很有用,可以避免没有必要的扩展
if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
expungeStaleEntries(); // 转移前再次清除
transfer(newTable, oldTable, false);
table = oldTable;
}
}
private void transfer(Entry[] src, Entry[] dest, boolean rehash) {
for (int j = 0; j < src.length; ++j) {
Entry e = src[j];
src[j] = null;
while (e != null) {
Entry next = e.next;
Object key = e.get();
if (key == null) { // 对于“key所引用的对象已回收”的关联键值对不转移
e.next = null; // Help GC
e.value = null; // " "
size--;
} else {
if (rehash) {
e.hash = hash(key);
}
int i = indexFor(e.hash, dest.length);
e.next = dest[i];
dest[i] = e;
}
e = next;
}
}
}
增删改查的操作过程与HashMap一样。
与HashMap基本一样,除了因为 若引用特性而采用的两个强引用:nextKey、currentKey,分别用于next()能获取到、正常使用时可以获取到:
private abstract class HashIterator implements Iterator {
private int index;
private Entry entry = null;
private Entry lastReturned = null;
private int expectedModCount = modCount;
/**
* Strong reference needed to avoid disappearance of key
* between hasNext and next
*/
private Object nextKey = null;
/**
* Strong reference needed to avoid disappearance of key
* between nextEntry() and any use of the entry
*/
private Object currentKey = null;
HashIterator() {
index = isEmpty() ? 0 : table.length;
}
public boolean hasNext() {
Entry[] t = table;
while (nextKey == null) {
Entry e = entry;
int i = index;
while (e == null && i > 0)
e = t[--i];
entry = e;
index = i;
if (e == null) {
currentKey = null;
return false;
}
nextKey = e.get(); // hold on to key in strong ref
if (nextKey == null)
entry = entry.next;
}
return true;
}
/** The common parts of next() across different types of iterators */
protected Entry nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextKey == null && !hasNext())
throw new NoSuchElementException();
lastReturned = entry;
entry = entry.next;
currentKey = nextKey;
nextKey = null;
return lastReturned;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
WeakHashMap.this.remove(currentKey);
expectedModCount = modCount;
lastReturned = null;
currentKey = null;
}
}
WeakHashMap的键值对的key为弱引用,其与HashMap的区别就是对key的这个特性的特殊处理。