创作宗旨:化繁为简,绝不冗余,点到为止

直接开门见山,就事论事!

什么是 HashMap?

HashMap类继承了父类AbstractMap<K,V>,实现了接口Map<K,V>, Cloneable, Serializable,AbstractMap<K,V>实现了对Map的基本操作,Serializable是序列化接口,可实现序列化,Map<K,V>接口规范了k-v对操作的抽象方法,Cloneable规范了继承了Object后去使用clone()方法前必须得实现Cloneable接口,否则抛出CloneNotSupportedException错误。

一、介绍

1. HashMap 的线程不安全的场景

  • 死锁

扩容产生死锁的原因关键有两点:
第一点:链表的头插法转移结点,JDK8版本就改成了尾插法;
第二点:多线程环境下,CPU的让出使得头结点的指针指向发生变化,但原线程重新占有CPU时仍然采取原先的做法;
解读:JDK8下使用尾插法,就算头结点已经转移了,但还是在头结点位置,也就是转移前后链表顺序不改变,只是多了一次重复的操作,结果不影响。
我不会用篇幅很长而做这么点事情,没必要,很简单的事 5 幅图就可以啦。
由于HashMap数组+链表的数据结构,旧桶是指数组下标所在位置。
线程 1 扩容操作

线程 1 转移结点A

线程1结点转移完毕

线程2开始转移结点A
因为是线程2首先要扩容的,不过CPU时间却让给了线程2,线程2也发觉需要扩容,等扩容完毕后再把CPU还给线程1

线程2转移结点A结束,环形链产生,也就是产生死锁。

所以原因的头插法得以佐证,CPU的轮询切换也演绎的很到位。
线程不安全讲完,下面就将原理,去潜移默化它。

2. HashMap 实现哈希命中均匀

哈希也就是khashcode计算后得到的下标,而hashcode是一个32位的int类型,要让分布均匀,即让不同的k不能出现有相同的效果,也就是得有唯一性,就要完全去利用高16位和低16位,而如果直接用32位去取模运算,高16就可能会用不到,导致hashcode16位不同的k依旧命中同一个桶位。
所以JDK8采取的做法是对高16位与低16位进行异或运算,这样就变相保留了高位和低位的所有特征了。
哈希均匀,JDK8之后

return (key == null) ? h = key.hashCode() ^ (h >>> 16);

取模运算,JDK7之前

h & (length - 1)

3. HashMap 参数以及扩容机制

初始容量16,达到阈值扩容,阈值等于最大容量 * 负载因子,扩容每次2倍,总是2n次方。

  • 扩容机制
    使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组元素拷贝到新的Entry数组里,Java1.7重新计算每个元素在数组中的位置。
    Java1.8它不需要重新计算每个元素,它会先比较元素的hashcode & 原来数组的大小的结果,如果为0,就以元素的原下标加入到新数组中,如果为1,就在原下标加上原数组的大小加入到新数组中,这样这两种情况就命中到新数组的前一半后一半了,也就是将原本全在原数组大小的所有元素又均匀分布一些元素到扩容未使用的下标中。
    那么就解释一下,假设加入的元素是随机的,也就是所有情况的hash值是均等出现的,而&原数组大小可以这样比如,有64个元素均等的从063,那么015分配给了扩容后数组大小为32的左半部分,16-31分配给了右半部分,32-48分配给了左半部分,48-63分配给了右半部分,就比较均匀分散在了两侧,而且机会均等,很巧妙!
// 分布到左侧
if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;
}
// 分布到右侧
else {if (hiTail == null)// 初始化hiHead = e;else// 尾插法hiTail.next = e;// 自增,下次尾插法必要的变量hiTail = e;
}

4. HashMap 的 get 和 put 操作

  • get

让我们见识下此时无图胜有图的效果吧!

get由源码解读是先获取keyhashCode,然后定位到对应的数组位置,再去遍历该元素处的链表
在这里才讲equals我觉得是最合适的,可以区分它与hashCode的区别,如果hashCode相同的两个元素在同一个数组下标,equals就开始发挥作用了,
不得不说,当然别说我是话痨,我觉得HashMap加入这两个的初衷是如果在一个庞大的Map中,要去get某一个keyvalue,有一种最直接的做法,就是直接整个Mapequals比较,这没错,而且还是有用的,但加入hashCode之后,hashCode能够给equals先筛选一番,因为不同的key,它的hash值一般是不同的,理解这里的一般,所以hash值不同的equals肯定就不同,就没必要再去对不同下标元素来equals比较了,好理解吧!
由于HashMap中新加入了红黑树,就多了一种getkey的方法,是一种二分法,对于大量数据的链表是不推荐的,所以采用红黑树(总是平衡的二叉树)更加地高效!

  • put

put的实现主要针对两种key,一种是不存在的key,一种是已存在的key
局部源码

public V put(K key, V value) {//采用hash(key)来计算key的hashCode值。return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {int h;//当key等于null的时候,不走hashCode()方法return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

如果为前者,而且key值为null,就在数组下标为0的链表中去寻找keynull的值,当然找不到,就创建keynull的结点。
如果为前者,而且key值不为null,就调用该元素hash计算具体的下标,在该下标的链表中寻找,当然也寻找不到,就尾插法加入该k-v结点。
如果为后者,即找到了,直接赋值就可以了,不需要考虑是否为null

这里要分开来put是因为,在HashTable上,是不允许有keynull的情况出现,因为当keynull时,key.hashCode()会报空指针异常。
所以在HashMap做了特别的处理,对keynull做了单独处理,这样就解决了keynull的情况。
还有一点,HashMapvalue如果设置为null,虽然你put了,但再去取key的时候,依旧是不存在!

二、总结

不足之处请大佬予以佐证,不希望哪个地方出现的错误误导同行,结果闹出笑话来,总之你们的阅读和评论是对作者最大的支持!
谢谢大家,我会继续努力,只为力争创作高质量的文章,分享给各位有需要的读者。
我的技术专栏:https://github.com/fyupeng

专注品质,热爱生活。
交流技术,寻求同志。

深入浅出~HashMap的底层原理透析相关推荐

  1. 深入浅入 ~ ConCurrentHashMap底层原理透析

    创作宗旨:化繁为简,绝不冗余,点到为止 ConcurrentHashMap<K,V> 继承了AbstractMap<K,V>,实现了ConcurrentMap<K,V&g ...

  2. java中数据类型byte的底层原理透析

    byte数据类型详解 二进制.位.字节的关系 二进制 位 字节 三者联系 ascll码表 Byte数据类型底层原理说明 代码与结果展示 结果透析 二进制.位.字节的关系 二进制 十进制:0,1,2,3 ...

  3. 华为交换机stp原理透析及实战

    转载来源 :华为交换机stp原理透析及实战 :https://mp.weixin.qq.com/s/hK2UhxssPb-ww6J0oCu1dQ 生成树协议stp,快速生成树协议rstp,多实例生成树 ...

  4. 为什么要学习HashMap的底层原理?

    本文转载自公众号  码农翻身 上周发了一篇文章<漫画:什么是HashMap?>,引起了不少人的讨论,有一个人的留言引发了我的思考:"作为一个程序员, 真的有必要学习这些底层原理吗 ...

  5. HashMap 的底层原理

    HashMap 的底层原理 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二 ...

  6. 为什么使用HashMap需要重写hashcode和equals方法_最通俗易懂搞定HashMap的底层原理...

    HashMap的底层原理面试必考题. 为什么面试官如此青睐这道题? HashMap里面涉及了很多的知识点,可以比较全面考察面试者的基本功,想要拿到一个好offer,这是一个迈不过的坎,接下来我用最通俗 ...

  7. hashMap和hashTable的区别以及HashMap的底层原理?

    hashMap和hashTable的区别? 1.继承的父类不同 HashTable继承Dictionary类,而hashMap继承了AbstractMap类,但是二者都实现了map接口. 2.线程安全 ...

  8. 精华推荐 | 【JVM深层系列】「GC底层调优系列」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)

    前提介绍 很多小伙伴,都跟我反馈,说自己总是对JVM这一块的学习和认识不够扎实也不够成熟,因为JVM的一些特性以及运作机制总是混淆以及不确定,导致面试和工作实战中出现了很多的纰漏和短板,解决广大小伙伴 ...

  9. HashMap的底层原理你真的知道?

    HashMap的底层实现原理是面试中出现频率非常高的一道面试题,本文将对HashMap的底层实现原理做一个简要的概况和总结,便于复习. 一.对于Map集合存储结构的理解 首先介绍以HashMap为典型 ...

最新文章

  1. RecyclerView的使用(1)之HelloWorld
  2. android坐标判断三角形,Android实现三角形气泡效果方式汇总
  3. php获取域名与路径
  4. Codeforces 785 D.Anton and School - 2(组合数处理)
  5. 为什么中国的程序员技术偏低
  6. U9cloud RPA加持 智领未来
  7. 两边同时取对数求复合函数_高中数学:对数运算三难点
  8. python sorted下标_全!Python基础之原生数据类型、判断和循环、函数和文件操作合集...
  9. Vue--findIndex方法的使用原理
  10. 四十八、微信小程序开发系统组件
  11. React 18 RC 版本发布啦,生产环境用起来!
  12. Greendao bean序列化出现的 问题!
  13. 5.11socket选项
  14. react-router的BrowserHistory 和 HashHistory 的区别,如何解决使用BrowserHistory 引起的访问路径问题
  15. html5变动标签新写法,HTML5新增的标签和废除的标签
  16. CSS选择器之基本选择器总结
  17. 最少拍控制系统设计(二)纹波问题分析与无纹波系统的设计与仿真(附matlab和simulink源文件)
  18. 计算机网络之子网掩码,子网掩码
  19. 内窥镜去反光的论文整理(转)
  20. 打印机如何打印白色_打印机可以打印白色吗?

热门文章

  1. 小马识途:问答营销有哪些注意事项有哪些?
  2. springboot医美容院预约管理系统java ssm
  3. 高级编程技术hw week 12
  4. 为什么Eureka是AP,zookeeper是CP
  5. 【券商报告】汽车行业比亚迪DM发布会点评:汽车纺锤形重心市场电动化有望加速——附下载链接
  6. 达观数据新用户推荐的三大利器
  7. 区块链项目包装方法有哪些?深入分析一站式包装技巧和手段
  8. ASEMI代理ADXL345BCCZ-RL7原装ADI车规级ADXL345BCCZ-RL7
  9. 无法启动MSVCP120.dll
  10. 识别病毒文件的四个非常不错的方法