弱引用之WeakHashMap的源码解析

1.Entry

  1. Entry本身是一个弱引用。弱引用WeakReference引用的对象即referent为Key。
  2. Value并非弱引用,而是强引用。
  3. Entry中的链表是为了解决hash冲突。
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() {//返回referentreturn (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) {// 判断是否为同一个Entry对象的方式:key & value 都相等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);}
}

put

public V put(K key, V value) {Object k = maskNull(key);int h = hash(k);Entry<K,V>[] tab = getTable();// 执行 expungeStaleEntriesint i = indexFor(h, tab.length);// 在 tab 中获取当前 key & value 对应的Entry对象for (Entry<K,V> e = tab[i]; e != null; e = e.next) {// 条件成立,说明存在hash冲突if (h == e.hash && eq(k, e.get())) {// 如果hash值一样,并且对象也相等,说明相同此时key完全一样,对其value进行覆盖V oldValue = e.value;if (value != oldValue)e.value = value;return oldValue;// 覆盖后返回旧值}// if条件不成立,则通过当前Entry即e中的next指针移动,遍历其链表上全部的Entry以此比较,直到next指针指向null,则退出循环}modCount++;Entry<K,V> e = tab[i];//如果上述for循环存在成立,此时表明hash存在冲突,但是整个链表内没有key & value 值跟当前key & value相等,此时将新生Entry添加到链表头部,其next指针指向e//如果上述for循环不成立,则表明hash不存在冲突,e为nulltab[i] = new Entry<>(k, value, queue, h, e);if (++size >= threshold)resize(tab.length * 2);return null;// 不存在覆盖则返回null
}

tab:Entry类型的数组。

判断key相同的条件:

  1. key的hash必须相等。
  2. 对象引用指向同一个对象视为相同Key。
  3. 步骤1成立 & 步骤2不成立 的情况下,通过Object的equals方法再次比较【扩展点】。如果没有在引用类型的Key中重写equals方法则等同于步骤2。如果重写了equals方法则即使对象引用不同,也可以通过重写equals方法返回想要的结果,此处就是使用方可扩展的点。

综上,针对引用类型的key,如果两个key引用值相等则新旧key肯定对应同一个Entry,值必须覆盖处理。如果两个key引用值不相等也可以对应一个Entry,值覆盖处理。

private Entry<K,V>[] getTable() {expungeStaleEntries();return table;
}
private static boolean eq(Object x, Object y) {return x == y || x.equals(y);
}

❓为啥不使用虚引用呢?

  1. 首先对于Reference的子类虚引用PhantomReference来说重写了抽象类Reference之get方法,并且返回值直接返回Null。
  2. referent即虚引用指向的对象只能通过抽象类Reference之get方法获取。
  3. WeakHashMap需要频繁利用抽象类Reference之get方法获取Key,目的是解决hash冲突后值覆盖条件的判断。

expungeStaleEntries

根据弱引用特性,发生GC时referent必被回收。同时WeakReference即Entry将会被添加至引用队列queue中。在WeakHashMap使用场景下Entry对象由于一直存在强引用是不会被回收的,但是Entry中的value属性必须进行回收,所以需要切断属性value存在的强引用,具体实现过程如下所示:

为啥在释放value的前提是通过比较Entry的引用呢?因为此时referent指向的Key已经被GC处理了。

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) {// 同一个Entry的前提下,将当前Entry的value置为null,方便GC,避免OOMif (prev == e)// 如果头结点就是目标节点table[i] = next;//移除掉当前节点elseprev.next = next;//目标节点非头结点e.value = null; // 辅助GC size--;break;}prev = p;p = next;}}}
}

调用方:put、size、resize涉及expungeStaleEntries方法的调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/22286.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STC8单片机无法驱动 LR7843的问题

情景. 淘宝购买&#xff08;替代继电器模块&#xff09;“隔离MOSFET MOS管 场效应管模块 LR7843”&#xff0c;但始终无法驱动。&#xff08;2023年8月5日) 起初怀疑模块坏了&#xff0c;io口的输出接继电器&#xff0c;继电器正常工作&#xff0c;但接该模块不工作。 后面还…

基于图片、无人机、摄像头拍摄进行智能检测功能

根据要求进行无人机拍摄的视频或图片进行智能识别&#xff0c;开发过程需要事项 1、根据图片案例进行标记&#xff0c;进行模型训练 2、视频模型训练 开发语言为python 根据需求功能进行测试结果如下 根据车辆识别标记进行的测试结果截图 测经过查看视频 8月1日

camunda-modeler(5.9.0)介绍及下载

官网地址: https://camunda.com/ 中文站点:http://camunda-cn.shaochenfeng.com Camunda Modeler是一个用于创建、编辑和验证BPMN、CMMN和DMN模型的工具。它提供了一个可视化的界面&#xff0c;使用户可以以图形方式设计和调整工作流程、决策表和案例管理模型。 具体来说&…

MySQL函数(二十五)

二八佳人体似酥&#xff0c;腰悬利剑斩愚夫&#xff0c;虽然不见人头落,暗里教君骨髓枯。 上一章简单介绍了 MySQL存储过程(二十四),如果没有看过,请观看上一章 前面学习了很多函数&#xff0c;使用这些函数可以对数据进行的各种处理操作&#xff0c;极大地提高用户对数据库的…

python可以做哪些小工具,python可以做什么小游戏

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python可以做什么好玩的&#xff0c;python可以做什么小游戏&#xff0c;今天让我们一起来看看吧&#xff01; 最近有几个友友问我说有没有比较好玩的Python小项目来练手&#xff0c;于是我找了几个比较有意思的给他们&…

Python二维数组的坑:vis = [[0]*m] * n

先来看&#xff0c;vis [[0]*m] * n&#xff0c; vis2 [[0]*m for _ in range(n)]有什么区别&#xff1f; 这两行代码都是用来创建二维列表&#xff08;或矩阵&#xff09;&#xff0c;但它们之间有一个关键的区别在于列表的复制方式。 vis [[0]*m] * n&#xff1a; 这种方…

阿里云平台注册及基础使用

首先进入阿里云官网&#xff1a; 阿里云-计算&#xff0c;为了无法计算的价值 点击右上角“登录/注册”&#xff0c;如果没有阿里云账号则需要注册。 注册界面&#xff1a; 注册完成后需要开通物联网平台公共实例&#xff1a; 注册成功后的登录&#xff1a; 同样点击右上角的…

mysql主从复制及原理

目录 主从复制原理实现主从复制 主从复制原理 主要基于MySQL二进制日志 主要包括三个线程&#xff08;2个I/O线程&#xff0c;1个SQL线程&#xff09; 1、MySQL将数据变化记录到二进制日志中&#xff1b; 2、Slave将MySQL的二进制日志拷贝到Slave的中继日志中&#xff1b; …

MySQL主从复制——概念、原理、搭建过程

文章目录 1.主从复制概念2.主从复制原理3.主从复制结构的搭建3.1 主库配置3.2 从库配置 4.测试主从复制是否搭建成功5.主从复制的小结 DML&#xff08;data manipulation language&#xff09;是数据操纵语言&#xff1a;它们是SELECT、UPDATE、INSERT、DELETE&#xff0c;就象…

java实现面板之间切换功能

本文实例为大家分享了java实现面板之间切换的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 如图&#xff1a; 关键技术&#xff1a;事件监听&#xff0c;设置显示面板&#xff0c;重新刷新验证。 ? 1 2 setContentPane(jp2);//设置显示的新面板 revalidate();/…

消息队列 -提供上层服务接口

目录 前言封装数据库封装内存操作内存的设计思想 应答模式 代码实现测试代码 前言 我们之前已经将 数据库 的操作 和文件的操作 都完成了, 但是对于上层调用来说, 并不关心是于数据库中存储数据还是往文件中存储数据, 因此 我们提供一个类, 封装一下 上述俩个类中的操作, 并将…

【计算机视觉】干货分享:Segmentation model PyTorch(快速搭建图像分割网络)

一、前言 如何快速搭建图像分割网络&#xff1f; 要手写把backbone &#xff0c;手写decoder 吗&#xff1f; 介绍一个分割神器&#xff0c;分分钟搭建一个分割网络。 仓库的地址&#xff1a; https://github.com/qubvel/segmentation_models.pytorch该库的主要特点是&#…

行业追踪,2023-08-04

自动复盘 2023-08-04 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

开源免费用|Apache Doris 2.0 推出跨集群数据复制功能

随着企业业务的发展&#xff0c;系统架构趋于复杂、数据规模不断增大&#xff0c;数据分布存储在不同的地域、数据中心或云平台上的现象越发普遍&#xff0c;如何保证数据的可靠性和在线服务的连续性成为人们关注的重点。在此基础上&#xff0c;跨集群复制&#xff08;Cross-Cl…

Mac环境变量问题

查询环境变量 echo $PATH 查询当前使用的Shell&#xff0c;这里注意SHELL需要大写 echo $SHELL >>>如果输出的是/bin/zsh&#xff0c;说明使用的是zsh。zsh读取的个人配置文件是~/.zshrc (mac10.15.x 后对应的是~/.zprofile) >>>如果输出的是/bin/bash&…

如何把非1024的采样数放入aac编码器

一. aac对数据规格要求 二、代码实现 1.初始化 2.填入数据 3.取数据 三.图解 一. aac对放入的采样数要求 我们知道aac每次接受的字节数是固定的&#xff0c;在之前的文章里有介绍libfdk_aac音频采样数和编码字节数注意 它支持的采样数和编码字节数分别是&#xff1a; fdk_aac …

微信小程序:点击按钮实现数据加载(带模糊查询)

效果图 代码 wxml: <!-- 搜索框--> <form action"" bindsubmit"search_all_productiond"><view class"search_position"><view class"search"><view class"search_left">工单号:</view…

Linux tcpdump 命令详解

简介 用简单的话来定义tcpdump&#xff0c;就是&#xff1a;dump the traffic on a network&#xff0c;根据使用者的定义对网络上的数据包进行截获的包分析工具。 tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的…

【Leetcode】(自食用)简单题||单词数

step by step. 题目&#xff1a; 统计字符串中的单词个数&#xff0c;这里的单词指的是连续的不是空格的字符。 请注意&#xff0c;你可以假定字符串里不包括任何不可打印的字符。 示例: 输入: "Hello, my name is John" 输出: 5 解释: 这里的单词是指连续的不是空格…

【C++】开源:sqlite3数据库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍sqlite3数据库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下…