你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(二)

简介: 你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(二)

③.HashMap算法


  • ①. HashMap是如何存数、找数、寻找下标的——此算法必须领悟并记住,这就是资本


  1. 如果key为null,则默认对应的数组下标为0


  1. key的hashCode值记为hashCode


  1. 记h = hashCode ^ (hashCode >>> 16)


  1. 记数组长度len也就是HashMap容量cap


  1. index = h & (cap-1)


微信图片_20220109134655.png

①. h = hashCode ^ (hashCode >>> 16)的理解


①. hashCode >>> 16:对hashCode值向右移16位,hashCode的值是一个32位的int,如果hashCode值比较小,那么高位就是0,相当于没有高位;如果hashCode的值比较大的话,高16就有数据


②. hashCode ^ (hashCode >>> 16):这个时候,hashCode与高16位进行^运算。高16位参与运算,可以大大提高离散的程度。


②. 为何不直接使用hashCode值来跟数组长度取模?


①. 如果直接使用hashCode和数组长度-1 进行运与运算,数组长度比较小的时候,这里我们使用数组长度为16。当hashCode很大的时候,高16位一直变动,而底16位处于不变的时候。因为数组长度16对应的后4位是1111。而我们hashCode低16位固定。固定的低16位和数组的后4位1111是一个固定的值,就是说元素储存的索引是固定的。元素都储存在同一个位置上,那产生的冲突将是非常恐怖的。意味着我们通过get( )去获取值,查询性能非常低,效率就也低


②. 所以,这个算法,在进行后面取模求index之前,先获取了一个更具有代表这个数据整体的数值h(h = hashCode ^ (hashCode >>> 16))。这样操作之后就将hashCode值的高低16位都运用了起来,这样h & (cap - 1)取模的离散算法就更优了,产生的冲突的几率更小,访问效率更高


③. 为什么index = h & (length-1) 要减去1?


①. 假如不减去1,length长度=16,这个时候和hashCode进行运与运算。16的后5位是10000,这个时候hashCode的后四位无论是什么结果都是4个0,而倒数第五位只有两个值,一个是0一个是1。如果是0那么对应储存的位置就是0;如果是1,对应储存的元素位置就是16。这个时候如果是0,那么所有的元素都储存在0位置,导致hash冲突,通过get()去查询元素时,效率极其低下。如果是1,对应储存的元素位置就是16,而索引最多只有15,所以会导致下标越界。


②. 如果index = h & (length-1) ,假如这个时候length=16,我们的后4位就是1111,这个时候hashCode的后四位无论怎么变化,h & (length-1) 运算的结果都是在[0-15]之间的。



相关文章
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(四)
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(四)
108 0
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(四)
|
存储 算法
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(三)
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(三)
112 0
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(三)
|
存储 Java
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(一)
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(一)
你要敢问我HashMap,那我绝对让你一脸懵 建议收藏(一)
|
2月前
|
Java
让星星⭐月亮告诉你,HashMap中保证红黑树根节点一定是对应链表头节点moveRootToFront()方法源码解读
当红黑树的根节点不是其对应链表的头节点时,通过调整指针的方式将其移动至链表头部。具体步骤包括:从链表中移除根节点,更新根节点及其前后节点的指针,确保根节点成为新的头节点,并保持链表结构的完整性。此过程在Java的`HashMap$TreeNode.moveRootToFront()`方法中实现,确保了高效的数据访问与管理。
30 2
|
2月前
|
Java 索引
让星星⭐月亮告诉你,HashMap之往红黑树添加元素-putTreeVal方法源码解读
本文详细解析了Java `HashMap` 中 `putTreeVal` 方法的源码,该方法用于在红黑树中添加元素。当数组索引位置已存在红黑树类型的元素时,会调用此方法。具体步骤包括:从根节点开始遍历红黑树,找到合适位置插入新元素,调整节点指针,保持红黑树平衡,并确保根节点是链表头节点。通过源码解析,帮助读者深入理解 `HashMap` 的内部实现机制。
37 2
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
7月前
|
存储 安全 Java
HashMap源码全面解析
HashMap源码全面解析
|
2月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
61 5
|
2月前
|
算法 索引
让星星⭐月亮告诉你,HashMap的resize()即扩容方法源码解读(已重新完善,如有不足之处,欢迎指正~)
`HashMap`的`resize()`方法主要用于数组扩容,包括初始化或加倍数组容量。该方法首先计算新的数组容量和扩容阈值,然后创建新数组。接着,旧数组中的数据根据`(e.hash & oldCap)`是否等于0被重新分配到新数组中,分为低位区和高位区两个链表,确保数据迁移时的正确性和高效性。
69 3
|
2月前
|
Java 索引
让星星⭐月亮告诉你,HashMap中红黑树TreeNode的split方法源码解读
本文详细解析了Java中`HashMap`的`TreeNode`类的`split`方法,该方法主要用于在`HashMap`扩容时将红黑树节点从旧数组迁移到新数组,并根据`(e.hash & oldCap)`的结果将节点分为低位和高位两个子树。低位子树如果元素数少于等于6,则进行去树化操作;若多于6且高位子树非空,则进行树化操作,确保数据结构的高效性。文中还介绍了`untreeify`和`replacementNode`方法,分别用于将红黑树节点转换为普通链表节点。
27 2