HashMap源码分析小结
HashMap相关问题
HashMap实现原理
HashMap是以键值对的形式存储数据,内部是通过数组+链表结构实现,在1.7之后的版本,链表结构可以升级为红黑树,提高查询效率
- key和value都支持为null;key为null时hash值是0,取模后也是0 ,也是就是会存放在数组第一个链表中
HashMap的put、get、remove过程
put过程:
-
先根据key值计算Hash值
-
再根据hash值与数组长度进行取模运算,计算出要落在数组哪个位置上
-
接着判断数组是否为空,为空的话对数组进行初始化,默认数组容量是16
-
然后判断该数组位置是否已经存在元素,如果不存在则直接创建Node结点放入数组对应位置
-
如果存在则继续判断是否是红黑树,如果是红黑树则在红黑树中创建或者更新Node结点
-
如果是链表结构,则先插入元素,然后判断链表中元素个数是否已经到达阈值8个,如果到达了,并且数组容量大于64个,则将当前链表升级为红黑树结构,如果不足64则进行一次扩容
-
在扩容时,红黑树会进行拆分,拆分过程中会判断红黑树中结点是否少于阈值6个,如果是的话变回链表结构
-
remove元素时判断根节点和左右子节点是否为null来决定是否转回链表结构,而没有根据阈值6来判断
-
-
最后插入完元素后,会判断当前元素总的个数是否达到阈值,默认是数组容量的3/4,如果达到了则进行扩容
- 3/4是根据空间和时间综合判定的,如果设置过小,则扩容频繁,如果设置过大,则hash冲突概率增加,查找效率更低
get过程:
-
先通过key计算hash值,然后与数组长度取模运算确定在数组中的位置
-
然后判断数组中对应元素key值是否相同,相同则返回该结点的value值
-
接着判断是否是红黑树,如果是的话从红黑树中查找该key对应结点
-
如果是链表则遍历链表中每一个元素找到key值相同的Node结点并返回value值
remove过程:
-
remove过程和查找差不多,也是先计算hash值,取模计算出数组位置,然后判断是否红黑树等等,找到元素后从原来位置删除
-
从红黑树中删除之后,会判断根节点以及其左右结点是否为空来决定要不要转回链表结构
容量转为2的n次幂
-
int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
-
现将设置的容量减1,然后不断进行右移和或运算,目的是将低位上的数都转为1(0000 1111);最后再+1,形成(0001 0000)这种结构,结果必然是2的n次方
Hash算法和Hash冲突问题
-
(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
-
key可以为null
-
hash值的计算:key的hash值与它的高16位右移后进行异或运算,目的是为了降低低16位相同的概率,从而减少hash碰撞
-
这是因为跟数组长度-1进行与运算时,数组长度-1的高位基本都是0,进行与运算后结果也是0 ,如果两个数高位不同,低位相同,就会导致计算结果一致,发生hash碰撞;
-
所以需要降低低位相同的概率,就能减少hash碰撞
-
-
为什么进行异或运算,而不是与运算或者或等其他运算?
-
因为如果进行与运算,会导致结果趋向于0更多
-
进行或运算,结果趋向于1更多
-
只有进行异或运算,结果0和1的个数会趋于一样多,这样结果随机性就更大,hash碰撞概率就小很多
-
-
降低hash冲突办法:
-
计算hash值的时候进行异或运算
-
降低负载因子(load factor),增加数组容量大小
-
计算数组中的位置
-
(n - 1) & hash
-
根据Hash值和数组长度减1进行与运算;相当于对数组长度取模运算,保证取模后结果在数组长度范围内;与运算速度要比取模运算快
- 数组容量大小是2的n次方,可以保证数组大小-1后与hash值与运算后结果在数组范围内,取代模运算,效率更高
HashMap扩容机制
-
扩容时会先判断容量是否有初始化,如果没有则先初始化为默认容量16或者传入的容量,容量最大不能超过2的30次方
-
然后判断当前容量是否超过阈值,默认是当前容量的3/4,如果超过则进行扩容,每次扩容会把容量增加到原来的2倍
-
接着会将原来数组中的数据根据hash值复制到扩容后的数组中,在拷贝数据过程中,原有链表或者红黑树会被拆分成两份,一部分会保存在原有数组位置,另一部分会存在当前数组位置加上原有数组容量大小的位置
-
根据if ((e.hash & oldCap) == 0) 判断链表中的元素是否需要移动,如果等于0则不移动,否则移动到当前数组位置加上旧数组容量大小的位置:newTab[j+oldCap]
- 因为同一个Hash值跟数组扩容前和扩容后的大小进行取模运算后,只有两种情况,要么跟原来位置不变,要么比原来位置多原来数组容量大小
-
扩容导致死循环问题
- 因为在1.7版本中,HashMap扩容时采用的是头插法,也就是拷贝旧数组中元素到新数组中时,新元素是插入到链表头部的,当并发时可能出现多个线程同时在扩容,当其中一个线程正在将元素A移动到新的位置时,A的下一个元素时B,另一个线程正在将B插入A的前面,但是A指向B的链接还没有断开,B就指向了A,这就会导致A和B互相链接着形成环状,当调用get方法遍历链表时就可能会卡死在这里永远无法退出循环
HashMap如何保证线程安全
HashTable
-
底层实现也是数组+链表
-
使用了Synchronized同步锁,会锁住整个HashTable对象,效率低
-
线程安全,key和value都不能为null
ConcurrentHashMap
-
线程安全
-
将整个Map分成N个段Segment保存在数组中,每个Segment又可以看做一个小型的HashMap,内部由数据+链表结构实现,Segment继承自可重入锁;
-
锁分段技术,每一个Segment都是一个可重入锁,每次只会锁住该段中的元素,不会影响到其他段中元素的读写
-
扩容采用段内扩容,每次扩容只针对当前Segment,不会对整个表扩容
-
有些操作需要锁定整个表,比如获取所有元素个数size,或者判断某个元素是否在表中containsValue操作
- 在计算size时会先尝试几次不加锁统计,当发现算了几次结果都一样时,则任务没有新增或者删除,如果有变化则强制将所有Segment加锁后再统计
-
jdk1.8后的变化:
-
参考地址:https://blog.csdn.net/qq_26542493/article/details/105651338&cd=1&hl=zh-CN&ct=clnk&gl=ph&strip=1
HashMap是否有序,如何保证有序?
无序的,LinkedHashMap可以保证有序
为什么容量必须是2的N次方
- 很多地方用到二进制运算,比如计算hash值,计算数组中的位置,扩容等;使用2的N次方转成二进制就是一个1,其他都是0,方便二进制运算
参考链接:https://blog.csdn.net/qq_26542493/article/details/105482732&cd=1&hl=zh-CN&ct=clnk&gl=ph&strip=1
相关文章:
HashMap源码分析小结
HashMap相关问题 HashMap实现原理 HashMap是以键值对的形式存储数据,内部是通过数组链表结构实现,在1.7之后的版本,链表结构可以升级为红黑树,提高查询效率 key和value都支持为null;key为null时hash值是0࿰…...

太奇怪了!小公司面试全挂,大厂面试全过,为什么小公司要求比大厂还高?...
大厂的人才去小公司面试,一定是降维打击吗?还真未必。一位网友很困惑:真的奇怪,小公司面试全挂,大厂面试10个过了9个,感觉小公司要求比大厂还高,这是怎么了?来看看网友们的看法。有人…...
Java开发环境配置
Java开发环境配置 Java是目前世界上最流行的编程语言之一,它的使用范围广泛,从Web应用程序到桌面应用程序再到移动应用程序,Java都是一种非常有用的语言。想要进行Java开发,首先需要在计算机上配置Java开发环境。 在本文中&…...

大学英语视听说教程(陈向京版本)
词汇题(55道) 1. You should carefully think over_____ the manager said at the meeting. A. that B. which C. what D. whose 1.选C,考察宾语从句连接词,主句谓语动词think over后面缺宾语,后面的宾语从句谓语动…...
nginx--开源免费
nginx开源免费,支持高性能,高并发的web服务和代理服务软件。 apache,nodejs nginx可以提供的服务: 1、web服务 2、负载均衡(反向代理)(动静分离) 3、web cache(web缓存) nginx…...

阿里云OSS对象存储
目录 1:OSS 1.1:开通OSS服务 1.2:搭建OSS环境 1.2.1:创建Bucket存储空间 1.2.2:创建文件夹上传图片 1.2.3:RAM访问控制 1.3:快速入门 1.3.1:下载SDK 1.3.2:搭建环…...

基于VHDL语言的汽车测速系统设计_kaic
摘 要 汽车是现代交通工具。车速是一项至关重要的指标。既影响着汽车运输的生产率,又关乎着汽车行驶有没有超速违章,还影响着汽车行驶时人们的人身安全。而伴随着我国国民的安全防范意识的逐步增强,人们也开始越来越关心因为汽车的超速而带来的极其严重…...

【数据结构】单链表(笔记总结)
👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:数据结构 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞&…...
Git操作之 git add 撤销、git commit 撤销
1、git add 添加多余文件 撤销操作 git reset HEAD 后面什么都不跟的,就是上一次add 里面的内容全部撤销 git reset HEAD XXX 后面跟文件名,就是对某个文件进行撤销 2、git commit 撤销操作 git reset --soft HEAD^ 这样就成功的撤销了commit操作 注…...
用PyTorch实现MNIST数据集手写数字识别
资源下载:用Pytorch实现MNIST数据集的手写数字识别介绍资源-CSDN文库 手写数字识别是一项相当普遍的应用,因为在现实生活中,我们经常需要对手写数字进行识别,例如在邮政服务中,我们需要对邮件上的邮政编码进行识别&am…...
leetcode3:无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…...

ChatGPT让现在的软件都土掉渣了
我们家有两个娃,每次我们想要出去时订个酒店时都好麻烦。我在某程上找,我先看有没有家庭房,但家庭房很少,而且有些家庭房实际上只能睡得下两大一小。普通房间能不能睡得下四个人,那可是得查看很多信息,如床…...

IU5708D低静态电流同步升压DC-DC 控制器
IU5708D是高性能宽输入范围 (4.5V~40V) 同步升压控制器,支持高达52V的输出电压。输出电压采用恒定频率电流模式脉宽调制(PWM) 控制来实现调节。 芯片通过外部定时电阻器或通过与外部时钟信号同步来设置开关频率。在电阻编程模式下,开关频率可从50KHz编程…...
ubuntu查看软件安装路径
ubuntu怎么查看软件安装位置在哪 - 服务器 - 亿速云 1、执行程序查看 在终端使用type执行软件程序查看。 type google-chrome 2、通过进程查看对应的软件程序 在终端使用以下命令查看所有进程名。 ps -e 再使用以下过滤命令查看对应的进程信息即可。 ps aux|grep 软件名 …...

动态规划总结
1,01背包dp(每件物品最多选一次): 因为背包为0 的时候,什么都装不了,所以为零 ,就是他们的最优解。 最后一个单元格为最后的答案。 01背包模板 public class Knapsack {public static int kn…...

分享:数据库存储与索引技术(一)存储模型与索引结构演变
欢迎访问 OceanBase 官网获取更多信息:https://www.oceanbase.com/ 本文来自OceanBase社区分享,仅限交流探讨。原作者马伟,长期从事互联网广告检索系统的研发,对数据库,编译器等领域也有浓厚兴趣。 文章目录综述传统单…...

ZeusAutoCode代码生成工具(开源)
ZeusAutoCode代码生成工具 一、简介 Zeus代码生成器是一款自动代码生成工具,旨在快速生成基础的CRUD代码,在此基础上也提供了一些高级功能,做到灵活配置,生成可扩展性强的代码。 后端是基于springboot、freemarker、mybatisplu…...

算法题记录
力扣的算法题:1154 给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。返回该日期是当年的第几天。 示例 1: 输入:date “2019-01-09” 输出:9 解释:给定日期是2019年的第九天。 示例…...

章节2 行走数据江湖,只需一行代码
目录6. 函数填充,计算列6.1 excel操作6.2 pandas操作16.3 pandas操作28. 数据筛选、过滤,[绘图前的必备功课]8.1 excel操作8.2 Python操作http://sa.mentorx.net 蔓藤教育6. 函数填充,计算列 书的编号、书的名字、标价、折扣、最终价钱 最终…...

springboot集成xx-job;
概念理解: xx-job是一个分布式任务调度平台。比如你有AB两个项目。 AB的定时任务就要在xx-job上个注册。同时AB要配置对应的依赖。 所以集成xx-job要分2步骤:第一步:先搭建xx-job服务 第二步,在A项目中导包并引用。 第一步&am…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...