Java容器解析系列(13) WeakHashMap详解
关于WeakHashMap其实没有太多可说的,其与HashMap大致相同,区别就在于:
对每个
key的引用方式为弱引用;关于
java4种引用方式,参考java Reference网上很多说 弱引用指向 Entry,这种说法是完全错误的
在
key被回收时,对应的value并没有回收,只有在调用WeakHashMap的方法时才会回收value;
具体请看下列源码解析:
/**
* 数据结构原理几乎与HashMap一致;
* WeakHashMap 不会阻止Entry.key被回收;
* WeakHashMap中的每个key都是通过weakreference引用;
*
* expuntgeStaleEntries():清除 key已被回收 的entry,进而entry.value也被回收
* WeakHashMap中所有的public的增删改查方法都直接或间接调用了expuntgeStaleEntries()方法;
*
* @since 1.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 size;
private int threshold;
private final float loadFactor;
// 当key被回收时,其Entry(继承自WeakReference)对象会被添加到该队列中
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
int modCount;
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
transient boolean useAltHashing;
transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
// 各种构造器,与HashMap没有太大区别,hash算法相关代码,省略......
// 清除那些key已经被回收的entry
// WeakHashMap 中所有的public的增删改查方法都直接或间接调用了expuntgeStaleEntries()方法
// 如果在添加元素到 WeakHashMap 后,如果不再调用任何该 WeakHashMap 的方法,
// 那么 Entry 不会被回收,进而 Entry.value 不会被回收
private void expungeStaleEntries() {
// weakreference被回收时,会被添加到其注册的referencequeue中
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;
// 既然key被清除了,这里把value也清除掉(帮助GC)
// 其实这里也可以不清除value,因为这里将entry从链表中移除了,等于entry被删除了,虚拟机在之后的某个时间会将entry回收掉
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
private Entry<K, V>[] getTable() {
expungeStaleEntries();
return table;
}
public int size() {
if (size == 0)
return 0;
expungeStaleEntries();
return size;
}
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();// getTable() 调用了 expungeStaleEntries()
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;
}
// 与HashMap极度相似的代码,省略......
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;
}
public void clear() {
// 清空队列中的所有weakreference
while (queue.poll() != null)
;
modCount++;
// 清空所有Entry
Arrays.fill(table, null);
size = 0;
while (queue.poll() != null)
;
}
// Entry为WeakReference的子类,作为弱引用指向key
private static class Entry<K, V> extends WeakReference<Object> implements Map.Entry<K, V> {
V value;
int hash;
Entry<K, V> next;
Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K, V> next) {
super(key, queue);// 这里就确定了该 WeakReference 指向 key (referent 变量值为 key)
this.value = value;// 对 value 的引用为强引用,所以必须手动回收(通过 expungeStaleEntries())
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 ((k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode()));
}
public String toString() {
return getKey() + "=" + getValue();
}
}
// 各种迭代器,遍历方式,省略......
}
Java容器解析系列(13) WeakHashMap详解的更多相关文章
- Java容器解析系列(11) HashMap 详解
本篇我们来介绍一个最常用的Map结构--HashMap 关于HashMap,关于其基本原理,网上对其进行讲解的博客非常多,且很多都写的比较好,所以.... 这里直接贴上地址: 关于hash算法: Ha ...
- Java容器解析系列(7) ArrayDeque 详解
ArrayDeque,从名字上就可以看出来,其是通过数组实现的双端队列,我们先来看其源码: /** 有自动扩容机制; 不是线程安全的; 不允许添加null; 作为栈使用时比java.util.Stac ...
- Java容器解析系列(9) PrioriyQueue详解
PriorityQueue:优先级队列; 在介绍该类之前,我们需要先了解一种数据结构--堆,在有些书上也直接称之为优先队列: 堆(Heap)是是具有下列性质的完全二叉树:每个结点的值都 >= 其 ...
- Java容器解析系列(14) IdentityHashMap详解
IdentityHashMap,使用什么的跟HashMap相同,主要不同点在于: 数据结构:使用一个数组table来存储 key:value,table[2k] 为key, table[2k + 1] ...
- Java容器解析系列(17) LruCache详解
在之前讲LinkedHashMap的时候,我们说起可以用来实现LRU(least recent used)算法,接下来我看一下其中的一个具体实现-----android sdk 中的LruCache. ...
- Java容器解析系列(12) LinkedHashMap 详解
LinkedHashMap继承自HashMap,除了提供HashMap的功能外,LinkedHashMap还是维护一个双向链表(实际为带头结点的双向循环链表),持有所有的键值对的引用: 这个双向链表定 ...
- Java容器解析系列(0) 开篇
最近刚好学习完成数据结构与算法相关内容: Data-Structures-and-Algorithm-Analysis 想结合Java中的容器类加深一下理解,因为之前对Java的容器类理解不是很深刻, ...
- Java容器解析系列(10) Map AbstractMap 详解
前面介绍了List和Queue相关源码,这篇开始,我们先来学习一种java集合中的除Collection外的另一个分支------Map,这一分支的类图结构如下: 这里为什么不先介绍Set相关:因为很 ...
- Java容器解析系列(6) Queue Deque AbstractQueue 详解
首先我们来看一下Queue接口: /** * @since 1.5 */ public interface Queue<E> extends Collection<E> { / ...
随机推荐
- 微信小程序样式wxss各种问题总结(不断更新)
1)加入其它样式文件 @import '/css/dialog.wxss'; //注意:必须结尾用分号 2)隐藏滚动条 ::-webkit-scrollbar { width:; height:; c ...
- 安装低版本Microsoft .NET Framework 4.5受阻解决方案
在VS目标框中找不到Microsoft .NET Framework 4.5,项目出错,安装受阻.... 1.Microsoft .NET Framework 安装了高版本后,低版本通过网上上下载的d ...
- 【Docker】Ubuntu16.04将Docker升级至最新版
1.使用curl升级到最新版 curl -fsSL https://get.docker.com/ | sh 2.重启Docker sudo systemctl restart docker 3.设置 ...
- slim中的请求头
请求头 每个 HTTP 请求都有请求头.这些元数据描述了 HTTP 请求,但在请求体中不可见.Slim 的 PSR 7 请求对象提供了几个检查请求头的方法. 获取所有的请求头,返回一个数组:getHe ...
- java中List 和 Set 的区别
a. 特性 两个接口都是继承自Collection,是常用来存放数据项的集合,主要区别如下: ① List和Set之间很重要的一个区别是是否允许重复元素的存在,在List中允许插入重复的元 ...
- IntelliJ IDEA使用说明
1.IntelliJ IDEA简介: IDEA 全称 IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手.代码自动提 ...
- org.apache.subversion.javahl.ClientException: Item is not readable 解决办法
在使用eclise安装的插件subclipse查看svn的提交历史记录的时候,提示org.apache.subversion.javahl.ClientException: Item is not r ...
- C#反射与特性(三):反射类型的成员
目录 1,获取类型的信息 1.1 类型的基类和接口 1.2 获取属性.字段成员 上一篇文章中,介绍如何获取 Type 类型,Type 类型是反射的基础. 本篇文章中,将使用 Type 去获取成员信息, ...
- python接口自动化测试-unittest-生成测试报告
用例的管理问题解决了后,接下来要考虑的就是报告我问题了,这里生成测试报告主要用到 HTMLTestRunner.py 这个模块,下面简单介绍一下如何使用: 一.下载HTMLTestRunner下载: ...
- 洛谷P1189 SEARCH 题解 迭代加深
题目链接:https://www.luogu.com.cn/problem/P1189 题目大意: 给你一个 \(n \times m\) 的矩阵,其中有一些格子可以走,一些各自不能走,然后有一个点是 ...