1、简介
1.1 介绍
WeakHashMap 继承于AbstractMap,实现了Map接口。
和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。 这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是: (01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。 实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。 (02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。 (03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。 这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。
和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。
1.2 构造
WeakHashMap()
WeakHashMap(int initialCapacity)
WeakHashMap(int initialCapacity, float loadFactor)
WeakHashMap(Map<? extends K,? extends V> m)
1.3 常用方法
void clear()
从这张地图中删除所有的映射。
boolean containsKey(
Object key)
如果此映射包含指定键的映射,则返回
true 。
boolean containsValue(
Object value)
如果此地图将一个或多个键映射到指定的值,则返回
true 。
Set<Map.Entry<K,V>> entrySet()
返回此地图中包含的映射的
Set视图。
void forEach(BiConsumer<? super K,? super V> action)
对此映射中的每个条目执行给定的操作,直到所有条目都被处理或操作引发异常。
V
get(
Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。
boolean isEmpty()
如果此地图不包含键值映射,则返回
true 。
Set<K> keySet()
返回此地图中包含的键的
Set视图。
V put(K
key, V value)
将指定的值与此映射中的指定键相关联。
void putAll(Map<? extends K,? extends V> m)
将指定地图的所有映射复制到此地图。
V remove(
Object key)
如果存在,则从此弱散列映射中删除密钥的映射。
void replaceAll(BiFunction<? super K,? super V,? extends V>
function)
将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。
int size()
返回此地图中键值映射的数量。
Collection<V> values()
返回此地图中包含的值的Collection视图。
1.4 继承关系
java.lang.Object
↳ java.util.AbstractMap<K, V>
↳ java.util.WeakHashMap<K, V>
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {}
1.5 类图
2、源代码
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {
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 threshold;
private final ReferenceQueue<Object> queue =
new ReferenceQueue<>();
private Entry<K,V>[]
newTable(
int n) {
return (Entry<K,V>[])
new Entry<?,?>[n];
}
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);
}
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);
}
private static final Object NULL_KEY =
new Object();
final
int hash(Object k) {
int h = k.hashCode();
h ^= (h >>>
20) ^ (h >>>
12);
return h ^ (h >>>
7) ^ (h >>>
4);
}
private static int indexFor(
int h,
int length) {
return h & (length-
1);
}
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;
e.
value =
null;
size--;
break;
}
prev = p;
p = next;
}
}
}
}
private Entry<K,V>[]
getTable() {
expungeStaleEntries();
return table;
}
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;
}
public boolean
containsKey(Object key) {
return getEntry(key) !=
null;
}
Entry<K,V> getEntry(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 && !(e.hash == h && eq(k, e.
get())))
e = e.next;
return e;
}
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;
}
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 {
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}
private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
for (
int j =
0; j < src.length; ++j) {
Entry<K,V> e = src[j];
src[j] =
null;
while (e !=
null) {
Entry<K,V> next = e.next;
Object key = e.
get();
if (key ==
null) {
e.next =
null;
e.
value =
null;
size--;
}
else {
int i = indexFor(e.hash, dest.length);
e.next = dest[i];
dest[i] = e;
}
e = next;
}
}
}
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;
if (h == e.hash && eq(k, e.
get())) {
modCount++;
size--;
if (prev == e)
tab[i] = next;
else
prev.next = next;
return e.
value;
}
prev = e;
e = next;
}
return null;
}
boolean removeMapping(Object o) {
if (!(o instanceof Map.Entry))
return false;
Entry<K,V>[] tab = getTable();
Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
Object k = maskNull(entry.getKey());
int h = hash(k);
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;
if (h == e.hash && e.equals(entry)) {
modCount++;
size--;
if (prev == e)
tab[i] = next;
else
prev.next = next;
return true;
}
prev = e;
e = next;
}
return false;
}
public void clear() {
while (queue.poll() !=
null)
;
modCount++;
Arrays.fill(table,
null);
size =
0;
while (queue.poll() !=
null)
;
}
public boolean
containsValue(Object
value) {
if (
value==
null)
return containsNullValue();
Entry<K,V>[] tab = getTable();
for (
int i = tab.length; i-- >
0;)
for (Entry<K,V> e = tab[i]; e !=
null; e = e.next)
if (
value.equals(e.
value))
return true;
return false;
}
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) {
super(key, queue);
this.
value =
value;
this.hash = hash;
this.next = next;
}
@SuppressWarnings(
"unchecked")
public K
getKey() {
return (K) WeakHashMap.unmaskNull(
get());
}
public V
getValue() {
return value;
}
public V
setValue(V newValue) {
V oldValue =
value;
value = newValue;
return oldValue;
}
public boolean
equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
K k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 !=
null && k1.equals(k2))) {
V v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 !=
null && v1.equals(v2)))
return true;
}
return false;
}
public int hashCode() {
K k = getKey();
V v = getValue();
return Objects.hashCode(k) ^ Objects.hashCode(v);
}
public String
toString() {
return getKey() +
"=" + getValue();
}
}
private abstract class HashIterator<T> implements Iterator<T> {
private int index;
private Entry<K,V> entry;
private Entry<K,V> lastReturned;
private int expectedModCount = modCount;
private Object nextKey;
private Object currentKey;
HashIterator() {
index = isEmpty() ?
0 : table.length;
}
public boolean
hasNext() {
Entry<K,V>[] t = table;
while (nextKey ==
null) {
Entry<K,V> 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();
if (nextKey ==
null)
entry = entry.next;
}
return true;
}
protected Entry<K,V>
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;
}
}
public Set<K>
keySet() {
Set<K> ks = keySet;
if (ks ==
null) {
ks =
new KeySet();
keySet = ks;
}
return ks;
}
public Collection<V>
values() {
Collection<V> vs = values;
if (vs ==
null) {
vs =
new Values();
values = vs;
}
return vs;
}
public Set<Map.Entry<K,V>>
entrySet() {
Set<Map.Entry<K,V>> es = entrySet;
return es !=
null ? es : (entrySet =
new EntrySet());
}
}
2.2 遍历
public static void main(String[] args) {
WeakHashMap map =
new WeakHashMap();
Object key,
value;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
key = (String)entry.getKey();
value = (Integer)entry.getValue();
}
Iterator iterKey = map.keySet().iterator();
while (iterKey.hasNext()) {
key = (String)iterKey.next();
value = (Integer)map.
get(key);
}
Collection c = map.values();
Iterator iterVal= c.iterator();
while (iter.hasNext()) {
value = iter.next();
}
}
2.3 总结
WeakHashMap与jdk1.7的hashmap类似,长度都为2的次方数组加链表形式
WeakHashMap的Entry继承自WeakReference且key是弱引用,并关联一个ReferenceQueue。因为key是弱引用,jvm进行一次gc时,会回收key,并将Entry放入ReferenceQueue。queue.poll()弹出Entry,WeakHashMap从table中找到弹出的Entry并删除。此时这个Entry将被彻底回收。(Entry继承自WeakReference条件很重要,删除主要在expungeStaleEntries())
WeakHashMap的key可以为null
参考: http://www.cnblogs.com/skywang12345/p/3311092.html http://blog.csdn.net/kjfcpua/article/details/8495199 http://blog.csdn.net/u012332679/article/details/57489179