HashMap就是这么简单【源码剖析】
前言
声明,本文用得是jdk1.8
前面已经讲了Collection的总览和剖析List集合以及散列表、Map集合、红黑树的基础了:
本篇主要讲解HashMap,以及涉及到一些与hashtable的比较~
看这篇文章之前最好是有点数据结构的基础:
当然了,如果讲得有错的地方还请大家多多包涵并不吝在评论去指正~
一、HashMap剖析
首先看看HashMap的顶部注释说了些什么:
再来看看HashMap的类继承图:
下面我们来看一下HashMap的属性:
成员属性有这么几个:
再来看一下hashMap的一个内部类Node:
我们知道Hash的底层是散列表,而在Java中散列表的实现是通过数组+链表的~
再来简单看看put方法就可以印证我们的说法了:数组+链表-->散列表
我们可以简单总结出HashMap:
- 无序,允许为null,非同步
- 底层由散列表(哈希表)实现
- 初始容量和装载因子对HashMap影响挺大的,设置小了不好,设置大了也不好
1.1HashMap构造方法
HashMap的构造方法有4个:
在上面的构造方法最后一行,我们会发现调用了tableSizeFor()
,我们进去看看:
这是位运算算法,具体流程可参考:
- https://www.cnblogs.com/loading4/p/6239441.html
- https://blog.csdn.net/fan2012huan/article/details/51097331
看完上面可能会感到奇怪的是:为啥是将2的整数幂的数赋给threshold?
- threshold这个成员变量是阈值,决定了是否要将散列表再散列。它的值应该是:
capacity * load factor
才对的。
其实这里仅仅是一个初始化,当创建哈希表的时候,它会重新赋值的:
至于别的构造方法都差不多,这里我就不细讲了:
1.2put方法
put方法可以说是HashMap的核心,我们来看看:
我们来看看它是怎么计算哈希值的:
为什么要这样干呢??我们一般来说直接将key作为哈希值不就好了吗,做异或运算是干嘛用的??
我们看下来:
我们是根据key的哈希值来保存在散列表中的,我们表默认的初始容量是16,要放到散列表中,就是0-15的位置上。也就是tab[i = (n - 1) & hash]
。可以发现的是:在做&
运算的时候,仅仅是后4位有效~那如果我们key的哈希值高位变化很大,低位变化很小。直接拿过去做&
运算,这就会导致计算出来的Hash值相同的很多。
而设计者将key的哈希值的高位也做了运算(与高16位做异或运算,使得在做&运算时,此时的低位实际上是高位与低位的结合),这就增加了随机性,减少了碰撞冲突的可能性!
下面我们再来看看流程是怎么样的:
新值覆盖旧值,返回旧值测试:
接下来我们看看resize()
方法,在初始化的时候要调用这个方法,当散列表元素大于capacity * load factor
的时候也是调用resize()
1.3get方法
接下来我们看看getNode()
是怎么实现的:
1.4remove方法
再来看看removeNode()
的实现:
二、HashMap与Hashtable对比
从存储结构和实现来讲基本上都是相同的。它和HashMap的最大的不同是它是线程安全的,另外它不允许key和value为null。Hashtable是个过时的集合类,不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换
Hashtable具体阅读源码可参考:
- https://blog.csdn.net/panweiwei1994/article/details/77427010
- https://blog.csdn.net/panweiwei1994/article/details/77428710
四、总结
在JDK8中HashMap的底层是:数组+链表(散列表)+红黑树
在散列表中有装载因子这么一个属性,当装载因子*初始容量小于散列表元素时,该散列表会再散列,扩容2倍!
装载因子的默认值是0.75,无论是初始大了还是初始小了对我们HashMap的性能都不好
- 装载因子初始值大了,可以减少散列表再散列(扩容的次数),但同时会导致散列冲突的可能性变大(散列冲突也是耗性能的一个操作,要得操作链表(红黑树)!
- 装载因子初始值小了,可以减小散列冲突的可能性,但同时扩容的次数可能就会变多!
初始容量的默认值是16,它也一样,无论初始大了还是小了,对我们的HashMap都是有影响的:
- 初始容量过大,那么遍历时我们的速度就会受影响~
- 初始容量过小,散列表再散列(扩容的次数)可能就变得多,扩容也是一件非常耗费性能的一件事~
从源码上我们可以发现:HashMap并不是直接拿key的哈希值来用的,它会将key的哈希值的高16位进行异或操作,使得我们将元素放入哈希表的时候增加了一定的随机性。
还要值得注意的是:并不是桶子上有8位元素的时候它就能变成红黑树,它得同时满足我们的散列表容量大于64才行的~
明天要是无意外的话,可能会写TreeMap,敬请期待哦~~~~
文章的目录导航:https://zhongfucheng.bitcron.com/post/shou-ji/gong-zhong-hao-wen-zhang-zheng-li
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。
谢谢支持了!希望能多介绍给其他有需要的朋友
参考资料:
- 《Core Java》
- https://blog.csdn.net/panweiwei1994/article/details/76555359
- https://zhuanlan.zhihu.com/p/28216267
- https://blog.csdn.net/fan2012huan/article/details/51097331
- https://www.cnblogs.com/chinajava/p/5808416.html
HashMap就是这么简单【源码剖析】的更多相关文章
- HashMap源码剖析
HashMap源码剖析 无论是在平时的练习还是项目当中,HashMap用的是非常的广,真可谓无处不在.平时用的时候只知道HashMap是用来存储键值对的,却不知道它的底层是如何实现的. 一.HashM ...
- 转:【Java集合源码剖析】HashMap源码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票 ...
- LinkedHashMap就这么简单【源码剖析】
前言 声明,本文用得是jdk1.8 前面已经讲了Collection的总览和剖析List集合以及散列表.Map集合.红黑树还有HashMap基础了: Collection总览 List集合就这么简单[ ...
- TreeMap就这么简单【源码剖析】
前言 声明,本文用得是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...
- 【Java集合源码剖析】HashMap源码剖析
转载出处:http://blog.csdn.net/ns_code/article/details/36034955 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-va ...
- Java HashSet和HashMap源码剖析
转自: Java HashSet和HashMap源码剖析 总体介绍 之所以把HashSet和HashMap放在一起讲解,是因为二者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说Ha ...
- 【转】Java集合:HashMap源码剖析
Java集合:HashMap源码剖析 一.HashMap概述二.HashMap的数据结构三.HashMap源码分析 1.关键属性 2.构造方法 3.存储数据 4.调 ...
- 【java集合框架源码剖析系列】java源码剖析之HashMap
前言:之所以打算写java集合框架源码剖析系列博客是因为自己反思了一下阿里内推一面的失败(估计没过,因为写此博客已距阿里巴巴一面一个星期),当时面试完之后感觉自己回答的挺好的,而且据面试官最后说的这几 ...
- JDK1.7的HashMap的put(key, value)源码剖析
目录 HashMap的put操作源码解析 1.官方文档 2.put(key, value) 3.完结 HashMap的put操作源码解析 1.官方文档 1.1.继承结构 java.lang.Objec ...
随机推荐
- HyperLedger Fabric 1.1 手动部署单机单节点
手动部署单机单节点 之前发布过官方的e2e部署方案,由于环境或是访问权限等各种问题,还是有相当一部分码友无法成功跑起来,故此,本章将来一次纯手动操作的集群部署. 主要需要的步骤如下: 1:环境整理 2 ...
- PHP开发程序员的学习路线
PHP开发程序员的学习路线 兄弟连PHP培训,简单为大家梳理了每个阶段PHP程序员的技术要求,来帮助很多PHP程序做对照设定学习成长目标. 第一阶段:基础阶段(基础PHP程序员) 重点:把LNMP搞熟 ...
- python web开发-flask中日志的使用
Flask使用日志记录的方式: 初始化flask应用实例 在flask中使用logger,需要初始化一个flask的应用 app = Flask(__name__) 2. 调用logger 直接调用l ...
- 【Pycharm】 写python代码的优秀IDE Pycharm
Pycharm 在用pycharm之前,我一直用的是本身也是由python写的ulipad做我的IDE,在linux上的话就直接用vim编辑器.但是碰到pycharm之后觉得这玩意儿太NB了,虽然说不 ...
- 分布式代码管理系统GIT
1.1Git安装 CentOS上 yum install -y epel-release; yum install git Ubuntu上 apt-get install git Windo ...
- java日期格式大全 format SimpleDateFormat
http://www.cnblogs.com/loveyakamoz/archive/2011/08/18/2145047.html
- [Scala] 实现 NDCG
一.关于 NDCG [LTR] 信息检索评价指标(RP/MAP/DCG/NDCG/RR/ERR) 二.代码实现 1.训练数据的加载解析 import scala.io.Source /* * 训练行数 ...
- python全栈开发-Day11 迭代器、生成器、面向过程编程
一. 迭代器 一 .迭代的概念 迭代器即迭代的工具,那什么是迭代呢? 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 while True: #只是单纯地重复,因而 ...
- Beta 第七天
今天遇到的困难: 构造新适配器的时候出现了某些崩溃的问题 ListView监听器有部分的Bug 今天完成的任务: 陈甘霖:完成相机调用和图库功能,完成阿尔法项目遗留下来的位置调用问题,实现百度定位 蔡 ...
- 网络1712--c语言函数作业总结
作业亮点 1.总体情况 很多同学在思路方面大部分写的都很详细,能够通过思路回顾自己的代码 大部分同学都认真完成PTA,也充分利用了函数来解题 大部分同学能够从上机考试中总结自己的失误和不足点,制订了自 ...