WeakHashMap
基于哈希表的Map接口实现,具有弱键。当WeakHashMap中的一个条目的键不再正常使用时,它将自动被删除。更准确地说,给定键的映射的存在不会阻止该键被垃圾收集器丢弃,也就是说,使其可完成、完成,然后再回收。当一个键被丢弃时,它的条目将有效地从映射中删除,因此这个类的行为与其他映射实现略有不同。
WeakHashMap正是由于使用的是弱引用,因此它的对象可能被随时回收。更直观的说,当使用 WeakHashMap 时,即使没有删除任何元素,它的尺寸、get方法也可能不一样。比如:
(1)调用两次size()方法返回不同的值;第一次为10,第二次就为8了。
(2)两次调用isEmpty()方法,第一次返回false,第二次返回true;
(3)两次调用containsKey()方法,第一次返回true,第二次返回false;
(4)两次调用get()方法,第一次返回一个value,第二次返回null;
- 线程不安全
- key 作为虚引用
- value 作为强引用
- key 和 value 都可以为 空值; 但是比较有意思的,他会首先判断 是否 为空,如果为空,就会变成 new Object(); 就会创建 一个 Object 对象 作为 key, get 和 put 都是这种。
- 默认大小,加载因子 和 hashmap 类似,只是底层实现就是 entry 数组和单链表,并且 key 是 弱引用
- 前插法
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>
基于哈希表的Map接口实现,·
具有弱键。当WeakHashMap中的一个条目的键不再正常使用时,它将自动被删除
。更准确地说,给定键的映射的存在不会阻止该键被垃圾收集器丢弃,也就是说,使其可完成、完成,然后再回收。当一个键被丢弃时,它的条目将有效地从映射中删除,只是
,因此这个类的行为与其他映射实现略有不同。支持空值和空键
。该类具有与HashMap类相似的性能特征,具有相同的初始容量和负载因子的效率参数。与大多数集合类一样,这个类不是同步的。可以使用集合构造同步的WeakHashMap.synchronizedMap方法。这个类主要用于关键对象,这些对象的equals方法使用==操作符测试对象标识。一旦丢弃了这样的键,就永远不能重新创建它,因此不可能在稍后的某个时间在WeakHashMap中查找该键,却惊奇地发现其条目被删除了。该类可以很好地处理其equals方法不基于对象标识的关键对象,比如字符串实例。然而,对于这种可重新创建的键对象,自动删除键已被丢弃的WeakHashMap条目可能会令人困惑
。WeakHashMap类的行为在一定程度上依赖于垃圾收集器的操作,因此几个熟悉的(但不是必需的)映射不变量对这个类无效。因为垃圾收集器可能在任何时候丢弃键,所以WeakHashMap的行为可能就像一个未知线程正在悄悄地删除条目。特别是,即使你同步WeakHashMap实例并调用其mutator方法,有可能大小方法返回较小值随着时间的推移,isEmpty方法返回false,然后真的,containsKey方法返回true,后来错误对于一个给定的键,get方法的返回值对于一个给定的关键但后来返回null,对于以前出现在映射中的键,put方法返回null,而remove方法返回false,以及对键集、值集合和条目集进行连续的检查,以产生依次更少的元素。WeakHashMap中的每个键对象都作为弱引用的引用间接存储。因此,只有在映射内外对键的弱引用被垃圾收集器清除后,键才会被自动删除。实现注意:WeakHashMap中的值对象由普通的强引用保存。因此,应该注意确保值对象不直接或间接地强引用它们自己的键,因为这将防止键被丢弃。注意,
值对象可以通过WeakHashMap本身间接引用它的键;也就是说,一个值对象可以强引用其他一些键对象,而这些键对象的关联值对象又强引用第一个值对象的键。如果映射中的值不依赖于持有对它们的强引用的映射,
一种处理方法是在插入之前将值本身包装在WeakReferences中,如:m。put(key, new WeakReference(value)),然后在每个get上展开包装。返回的迭代器返回的集合的迭代器方法这门课的所有“集合视图方法”是快速失败:如果结构修改地图创建迭代器后,任何时候以任何方式除非通过迭代器的删除方法,迭代器将抛出ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在未来某个不确定的时间冒险做出任意的、不确定的行为。注意,迭代器的快速失败行为不能得到保证,因为一般来说,在存在非同步并发修改时不可能做出任何硬性保证。快速失败迭代器尽最大努力抛出ConcurrentModificationException。因此,如果要编写一个依赖于这个异常来保证其正确性的程序,那就错了:迭代器的快速失败行为应该仅用于检测错误。
/**
* The default initial capacity -- MUST be a power of two.
*/
private static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
*/
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
Entry<K,V>[] table;
/**
* The number of key-value mappings contained in this weak hash map.
*/
private int size;
/**
* The next size value at which to resize (capacity * load factor).
*/
private int threshold;
/**
* The load factor for the hash table.
*/
private final float loadFactor;
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) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
}
get
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;
}
put
采用尾插法
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;
}
resize
首先增长容量,然后 transfer
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 ignoring null elements and processing ref queue caused massive
* shrinkage, then restore old table. This should be rare, but avoids
* unbounded expansion of garbage-filled tables.
*/
if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}
transfer
会将一些 key == null 的键值对 去掉
/** Transfers all entries from src to dest tables */
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; // Help GC
e.value = null; // " "
size--;
} else {
int i = indexFor(e.hash, dest.length);
e.next = dest[i];
dest[i] = e;
}
e = next;
}
}
}
size
调用两次size()方法返回不同的值;第一次为10,第二次就为8了。
public int size() {
if (size == 0)
return 0;
expungeStaleEntries();
return size;
}
WeakHashMap 的使用
Integer
集合中只会存储 基本类型的封装类型,因此,在直接插入 基本类型的数据时候,会首先转变成相应的封装类型,并且,比较有意思的是, byte, short, int, long 类型对象的封装类型都有 相应的缓存,
每次插入 map.put(20, 20) 和 map.put(20, 20) 都会从缓存中获取 20 的封装对象;
也就是说 -128 - 127 之间的对象 integer 都是至少有一个 对象引用的,因此,下面的例子中,插入的 4 个数在垃圾回收时没有变化
WeakHashMap<Integer, Integer> weakHashMap = new WeakHashMap<>();
weakHashMap.put(20, 20);
weakHashMap.put(22, 22);
weakHashMap.put(24, 24);
weakHashMap.put(25, 25);
System.out.println(weakHashMap);
System.out.println(weakHashMap.size());
System.out.println(weakHashMap.keySet().size());
System.gc();
System.out.println(weakHashMap);
System.out.println(weakHashMap.size());
System.out.println(weakHashMap.keySet().size());
/*
{24=24, 25=25, 22=22, 20=20}
4
4
{24=24, 25=25, 22=22, 20=20}
4
4
*/
我们发现,只有 25 -24 键值对没有被删除,因为 存在于缓存中,而其他的对象都被垃圾回收
WeakHashMap<Integer, Integer> weakHashMap = new WeakHashMap<>();
weakHashMap.put(168, 20);
weakHashMap.put(695, 22);
weakHashMap.put(25, 24);
weakHashMap.put(36942, 25);
System.out.println(weakHashMap);
System.out.println(weakHashMap.size());
System.out.println(weakHashMap.keySet().size());
System.gc();
System.out.println(weakHashMap);
System.out.println(weakHashMap.size());
System.out.println(weakHashMap.keySet().size());
/*
{695=22, 25=24, 36942=25, 168=20}
4
4
{25=24}
1
1
*/
其他任意对象,如果,外部没有GC 可达,就会被回收
WeakHashMap<Dog, Integer> weakHashMap = new WeakHashMap<>();
weakHashMap.put(new Dog("CaoBourne1", 25), 30);
weakHashMap.put(new Dog("CaoBourne2", 25), 30);
weakHashMap.put(new Dog("CaoBourne3", 25), 30);
weakHashMap.put(new Dog("CaoBourne4", 25), 30);
System.out.println(weakHashMap);
System.out.println(weakHashMap.size());
System.out.println(weakHashMap.keySet().size());
System.gc();
System.out.println(weakHashMap);
System.out.println(weakHashMap.size());
System.out.println(weakHashMap.keySet().size());