解析源码,彻底弄懂HashMap(持续更新中)
为啥突然想着看HashMap源码了?
无意间看到有人说HashMap能考验Java程序员的基本功,之前我作为面试官帮公司招人的时候偶尔问起HashMap,大部分人回答基本都会用,且多数仅停留在put,get使用上面,实际上HashMap涉及的知识远远不止put和get那么简单。HashMap涉及线程、内存模型、Hash计算、链表结构、二进制运算等问题,可以说HashMap能考验一个Java程序员的技术功底。作为具备一定工作经验的技术人员,我们必须回头去恶补基础,凡是技术点都要努力去搞清楚是什么,为什么,怎么用。
HashMap基本概念及原理:
如果我们想要很快的查询一个数据,最好将其用数组存储,因为数组查询速度快,但是数组的长度不可以修改,所以它添加元素很麻烦,需要创建一个更大的数组,然后把老数组的元素按顺序拷贝到新数组中,而我们想要添加元素,最好使用链表去存储,因为链表是离散的,所以在添加或者删除的时候,只会修改局部的内容,也正是因为链表是离散的,它的位置在内存中不是一直固定的(指的是不连续),每次要查找下一个元素的时候,都需要读取其位置信息,所以链表的查询很慢。那有没有一种数据结构,它的查询很快,添加和删除速度也很快呢?答案是肯定的,结合数组和链表的优点,哈希表诞生了。
HashMap基于哈希表的Map接口实现,是以key-value的存储形式存在,即主要用来存放键值对。HashMap的实现不是同步的,这意味着它不是线程安全的。数组是HashMap的主体,链表则是为了解决hash冲突而存在的,所谓hash冲突就是两个对象调用hashCode()方法计算的hash值相同导致计算的数组索引也相同。
JDK1.8之后在解决Hash冲突时有了较大的变化,当链表长度大于边界值(默认为8)且当前数组长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。另外需要注意的是,当链表长度大于8但是数组长度小于64,此时也并不会将链表变成红黑树,而是选择扩容。这样做的目的是提高性能和较少搜索时间,具体可参照treeifyBin()方法。说了这么多,那HashMap的基本原理是怎样的呢?简单粗暴概括一下:
1、首先判断key是否为Null,如果为null,直接查找Enrty[0],如果不是Null,先计算Key的HashCode,得到Hash值,Hash值是一个int值。
2、根据Hash值,要找到对应的数组,所以对Entry[]的长度length取模(类似求余的算法,后面详细介绍),得到的就是Entry数组的index。
3、找到对应的数组就找到了所在的链表,然后按照链表的操作对Value进行插入、删除和查询操作。
HashMap底层数据结构及存储过程(以上纯属扯淡,下面重点来了):
JDK1.8之前HashMap底层由数组+链表实现
JDK1.8之后为了提高效率,底层由数组+链表+红黑树实现
在创建HashMap集合对象的时候,在JDK1.8之前是在构造方法中创建一个长度为16的Entry[] table来存储键值对,在JDK1.8之后不在构造方法中创建数组了,而是在第一次调用put()方法时创建数组Node[] table 用来存储键值对。


假设向哈希表中存储键值对key为zhangsan,value为28,根据zhangsan.hashCode()方法计算出hash值,然后结合数组长度采用取模的算法计算出zhangsan在Node数组中的索引值,如果计算出的索引没有值,则直接将28存储到数组中。那么,取模算法到底是怎样的呢?看下图。

红色框出来的代码告诉我们,采用的是按位与运算计算出索引值,其实就是我们熟知的取余法,但是为什么没有直接使用hash%length直接取余呢,是因为与运算效率更高,与运算规则:相同的二进制数位上都是1时结果为1,否则为0。在某种条件下hash%length等于n-1&hash,什么条件呢?那就是HashMap要求的数组长度length必须为2的n次幂,HashMap的构造函数允许我们自定义数组长度,但是它会检测然后自动帮我们把设置的长度往上转成最近的2的n次幂,比如我们初始化一个HashMap对象,设置数组长度为10,显然10不是2的某次幂,这时候会自动向上转成最近的2的某次幂,也就是16。
HashMap<String,String> hashMap = new HashMap<>(10);
解析源码,彻底弄懂HashMap(持续更新中)的更多相关文章
- Android源码编译常见错误(持续更新)
本文为个人工作中处理遇到的编译问题做个小结,后续遇到新的问题,持续更新. No such file or directory: 1. 检查路径是否有问题,文件是否存在,若文件存在且路径没问题 2. 检 ...
- IntelliJ IDEA 2019.2.1 破解教程, 最新激活码(激活到2089年8月,亲测有效,持续更新中...)
当前最新版本 IDEA 2019.2.1 本来笔者这边是有个正版激活码可以使用的,但是,2019.9月3号的时候,一些小伙伴反映这个注册码已经失效了,于是拿着自己的 IDEA, 赶快测试了一下,果不其 ...
- IDEA 2019.2破解激活教程(激活到2089年8月,亲测有效,持续更新中...)
本来笔者这边是有个正版激活码可以使用的,但是,2019.9月3号的时候,一些小伙伴反映这个注册码已经失效了,于是拿着自己的 IDEA, 赶快测试了一下,果不其然,已然是不能用了. 好在,笔者又找到了新 ...
- Flink 源码解析 —— 源码编译运行
更新一篇知识星球里面的源码分析文章,去年写的,周末自己录了个视频,大家看下效果好吗?如果好的话,后面补录发在知识星球里面的其他源码解析文章. 前言 之前自己本地 clone 了 Flink 的源码,编 ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- 【JDK1.8】 Java小白的源码学习系列:HashMap
目录 Java小白的源码学习系列:HashMap 官方文档解读 基本数据结构 基本源码解读 基本成员变量 构造器 巧妙的tableSizeFor put方法 巧妙的hash方法 JDK1.8的putV ...
- Apache源码包在LINUX(CENTOS6.8)中的安装(出现问题及解决)
任务:在CENT6.8系统中安装Apache(版本为:httpd-2.4.41) 前提:由于源码包必须先编译后安装,所以必须先安装编译器:gcc 理论步骤: 1.检测gcc软件包,如果不存在则进行安装 ...
- 直播平台源码搭建教程:微信小程序中的直播如何去掉水印
直播平台源码搭建教程:微信小程序中的直播如何去掉水印 本文与大家分享一下直播平台源码搭建教程,如何去掉直播视频的水印 var services = require('../../lib/service ...
- fastadmin 后台管理框架使用技巧(持续更新中)
fastadmin 后台管理框架使用技巧(持续更新中) FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架,具体介绍,请查看文档,文档地址为:https://doc. ...
- 2020年腾讯实习生C++面试题&持续更新中(5)
2020年腾讯实习生C++面试题&持续更新中(5) 大家好呀,我是好好学习天天编程的天天~ 昨天一位小伙伴反馈已经拿到了腾讯offer,很是替小伙伴的激动~ 那今天还是持续给大家分享面经,希望 ...
随机推荐
- failed to load main-class manifest attribute(运行jar包出错)
原因描述:MANIFEST.MF文件中的Main-Class配置不正确或格式不正确 检查方式:以WinRarR的方式打开jar包,如图所示, 点击进入箭头所指的META-INF文件夹 将MAN ...
- [JS奇怪的世界]No.55 危險小叮嚀:陣列與for in
前言 前面已經瞭解了使用內建函數建構子的某些危險地方,但其實陣列與for in,也是有一些危險的地方. 陣列與for in 在前面幾個章節有講過陣列就是物件,所以我們一樣可以使用 for in來做處理 ...
- 微软手机 能靠Surface Phone卷土重来吗?
能靠Surface Phone卷土重来吗?" title="微软手机 能靠Surface Phone卷土重来吗?"> 就算整体大环境再好,就算是站在风口之上,也总是 ...
- Intellij IDEA创建 Web 项目
快速构建 Web 项目 打开IDEA,新建Project,左边菜单栏选择 Maven,直接点 Next 选择GroupId和ArtifactId 选择项目名称,默认会填上工程位置.模块姓名等,直接点F ...
- Taro_Mall 是一款多端开源在线商城小程序.
介绍 Taro_Mall是一款多端开源在线商城应用程序,后台是基于litemall基础上进行开发,前端采用Taro框架编写,现已全部完成小程序和h5移动端,后续会对APP,淘宝,头条,百度小程序进行适 ...
- 对象深拷贝deepCopy
function type(obj){ return Object.prototype.toString.call(obj).slice(8,-1); } function deepCopy(targ ...
- Linux永久开放端口
/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT /sbin/iptables -I INPUT -p tcp --dport 3306 -j A ...
- 微信小程序学习 动手撸一个校园网小程序
动手撸一个校园网微信小程序 高考完毕,想必广大学子和家长们都在忙着查询各所高校的信息,刚好上手微信小程序,当练手也当为自己的学校做点宣传,便当即撸了一个校园网微信小程序. 效果预览 源码地址:Gith ...
- proteus pro 8.9 安装及汉化教程
最近由于网上上课老师要求我们自己安装proteus这款仿真软件,所以笔者也安装了最新款版的proteus pro 8.9,分享给大家安装心得,也包含汉化过程,希望大家能用软件好好学习. 备注:感谢博主 ...
- 使用AtomicStampedReference<T>的大坑
//在初始化的时候会把引用和时间戳存到pair中 AtomicStampedReference<Integer> integerAtomicStampedReference = new A ...