WeakHashMap源码分析
WeakHashMap是一种弱引用map,内部的key会存储为弱引用,
当jvm gc的时候,如果这些key没有强引用存在的话,会被gc回收掉,
下一次当我们操作map的时候会把对应的Entry整个删除掉,基于这种特性,WeakHashMap特别适用于缓存处理。
//初始化容量
private static final int DEFAULT_INITIAL_CAPACITY = ; //最大容量
private static final int MAXIMUM_CAPACITY = << ; //扩容因子
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
//内部类 Entry<K,V>[] table; //长度
private int size; //扩容银子
private int threshold; //装载因子
private final float loadFactor; //引用队列
private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); //修改次数
int modCount;
(1)容量
容量为数组的长度,亦即桶的个数,默认为16,最大为2的30次方,当容量达到64时才可以树化。
(2)装载因子
装载因子用来计算容量达到多少时才进行扩容,默认装载因子为0.75。
(3)引用队列
当弱键失效的时候会把Entry添加到这个队列中,当下次访问map的时候会把失效的Entry清除掉。
(4 ) entry为内部类
构造方法
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < )
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
int capacity = ;
while (capacity < initialCapacity)
capacity <<= ;
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) + ,
DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR);
putAll(m);
}
put方法:
public V put(K key, V value) {
//如果为空,用空代替对象
Object k = maskNull(key);
//计算hash指
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)
如果插入元素数量达到了句扩容至hash桶的两倍大小
resize(tab.length * );
return null;
}
(1)计算hash;
这里与HashMap有所不同,HashMap中如果key为空直接返回0,这里是用空对象来计算的。
另外打散方式也不同,HashMap只用了一次异或,这里用了四次,HashMap给出的解释是一次够了,而且就算冲突了也会转换成红黑树,对效率没什么影响。
(2)计算在哪个桶中;
(3)遍历桶对应的链表;
(4)如果找到元素就用新值替换旧值,并返回旧值;
(5)如果没找到就在链表头部插入新元素;
HashMap就插入到链表尾部。
(6)如果元素数量达到了扩容门槛,就把容量扩大到2倍大小;
HashMap中是大于threshold才扩容,这里等于threshold就开始扩容了。
扩容:
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 / ) {
threshold = (int)(newCapacity * loadFactor);
} else {
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}
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;
// 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;
}
}
}
}
private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
for (int j = ; 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;
}
}
}
(1)判断旧容量是否达到最大容量;
(2)新建新桶并把元素全部转移到新桶中;
(3)如果转移后元素个数不到扩容门槛的一半,则把元素再转移回旧桶,继续使用旧桶,说明不需要扩容;
(4)否则使用新桶,并计算新的扩容门槛;
(5)转移元素的过程中会把key为null的元素清除掉,所以size会变小;
expungeStaleEntries
()1)当key失效的时候gc会自动把对应的Entry添加到这个引用队列中;
(2)所有对map的操作都会直接或间接地调用到这个方法先移除失效的Entry,比如getTable()、size()、resize();
(3)这个方法的目的就是遍历引用队列,并把其中保存的Entry从map中移除掉,具体的过程请看类注释;
(4)从这里可以看到移除Entry的同时把value也一并置为null帮助gc清理元素,防御性编程。
总结:
(1)WeakHashMap使用(数组 + 链表)存储结构,并不是像hashmap一样,有红黑树的加入;
(2)WeakHashMap中的key是弱引用,gc的时候会被清除;
(3)每次对map的操作都会剔除失效key对应的Entry;
(4)使用String作为key时,一定要使用new String()这样的方式声明key,才会失效,其它的基本类型的包装类型是一样的;
WeakHashMap源码分析的更多相关文章
- 死磕 java集合之WeakHashMap源码分析
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 WeakHashMap是一种弱引用map,内部的key会存储为弱引用,当jvm gc的时 ...
- WeakHashMap 源码分析
WeakHashMap WeakHashMap 能解决什么问题?什么时候使用 WeakHashMap? 1)WeakHashMap 是基于弱引用键实现 Map 接口的哈希表.当内存紧张,并且键只被 W ...
- JDK源码分析(9)之 WeakHashMap 相关
平时我们使用最多的数据结构肯定是 HashMap,但是在使用的时候我们必须知道每个键值对的生命周期,并且手动清除它:但是如果我们不是很清楚它的生命周期,这时候就比较麻烦:通常有这样几种处理方式: 由一 ...
- Java集合源码分析(八)——WeakHashMap
简介 WeakHashMap 继承于AbstractMap,实现了Map接口. 和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和 ...
- MyBatis源码分析(3)—— Cache接口以及实现
@(MyBatis)[Cache] MyBatis源码分析--Cache接口以及实现 Cache接口 MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口. ...
- spring源码分析之spring-core总结篇
1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...
- cglib源码分析(一): 缓存和KEY
cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator.研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似. 一. C ...
- Java Reference 源码分析
@(Java)[Reference] Java Reference 源码分析 Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使 ...
- Hessian源码分析--HessianProxy
在上一篇博客 Hessian源码分析--HessianProxyFactory 中我们了解到,客户端获得的对象其实是HessianProxy生成的目标对象,当调用目标对象的方法时,会调用Hessian ...
随机推荐
- WIFI配置专项测试
1. 测试项目 A. 路由器加密方式测试: B. 路由器信道选择测试: C. DHCP过期时间测试 D. 路由器WIFI密码复杂度测试: 2. 测试结果 2.1. 加密方式测试 路由器密码为简单,其他 ...
- C++ STL 全排列函数
C++ 全排列函数...一听名字就在<algorithm>中... 首先第一个说的是next_permutation: #include <algorithm> bool n ...
- Java设计模式(2)——工厂方法模式
工厂方法模式同样属于类的创建型模式又被称为多态工厂模式.工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色, ...
- $.each()与$(selector).each()区别
jQuery.each( collection, callback(indexInArray, valueOfElement) )可用于迭代任何集合,无论是“名/值”对象(JavaScript对象)或 ...
- Introduction MBG
文档地址 http://www.mybatis.org/generator/configreference/xmlconfig.html 源码地址 https://github.com/mybatis ...
- mysql转ElasticSearch的分析 及JAVA API 初探
前言 最近工作中在进行一些技术优化,为了减少对数据库的压力,对于只读操作,在程序与db之间加了一层-ElasticSearch.具体实现是db与es通过bin-log进行同步,保证数据一致性,代码调用 ...
- (原创)Log4Net 在多层项目中的使用小记
这几天刚好在调整一个项目,把一些自己不是很清楚的东西先试验一下,这篇文章主要是对我在项目中需要使用Log4Net的一些记录.网上有很多相关的教程,但是各有各的说法,我结合我自己这个项目的需要,首先,项 ...
- 【Newtonsoft.Json.dll】操作列表JSON数据
JObject data = JObject.Parse(json); JArray array = JArray.Parse(data["list"] + "" ...
- Linux下卸载删除.Net Core
最近在技术博客和技术交流群遇到很多小伙伴们在Linux下更新或者安装.Net Core SDK后dotnet命令无法识别等问题,现如下解决: 卸载SDK命令 sudo yum remove dotne ...
- 并行架构技术 EasyMR
EasyMR简介 一个并行计算软件框架 基于此框架的软件可以运行在多台计算机组成的计算集群上,并且每个计算机的计算任务也是多线程方式并行进行的,对于处理复杂的业务和巨量数据时非常用于. 有着MapRe ...