HashMap相关的一系列问题

1.底层实现

1.7 数组 链表

1.8 数组 链表 红黑树

当集合要添加新的元素时,先调用这个元素的hashCode方法,如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

1、相同的对象必须具有相等的哈希码(或者散列码)。

2、如果两个对象的hashCode相同,它们并不一定相同。

存取:两次hash–> 用capacity取余–> 得出桶下标

扩容后是会计算桶下标的 可以解决链表过长

image-20230221175242217

桶容量大于

2.为什么用红黑树

原来的链表那种查找太慢了

3.什么时候会扩容

Hashmap的扩容需要满足两个条件:

当前数据存储的数量(即size())大小必须大于等于阈值;(默认大小为16,负载因子0.75,阈值12

当前加入的数据是否发生了hash冲突。(17会判断这个,18只要超过阈值就会扩容,不管冲突没)

4.什么时候会树化

  • 长度超过树化阈值8
  • 整个数组长度>= 64 (小于64会先扩容,来解决链表过长)

5.树化阈值为什么是8

受到攻击用户实现这种不好的hash算法 时可以保证一定的性能。 尽量使用链表

hash值如果足够随机,则在hash表内按泊松分布,在负载因子0.75的情况下,长度超过8的链表出现概率是0.00000006,选择8就是为了让树化几率足够小。

源码中的解释:

当hashCode离散性很好的时候,树型bin用到的概率非常小,因为数据均匀分布在每个bin中,几乎不会有bin中链表长度会达到阈值。但是在随机hashCode下,离散性可能会变差,然而JDK又不能阻止用户实现这种不好的hash算法,因此就可能导致不均匀的数据分布。不过理想情况下随机hashCode算法下所有bin中节点的分布频率会遵循泊松分布,我们可以看到,一个bin中链表长度达到8个元素的概率为0.00000006,几乎是不可能事件。

5.什么时候会退化成链表

  • 扩容后 导致树拆分 长度<=6

  • remove树节点之前

    若root、, root.left、 root.right、 root.left.left有一个为null,也会退化为链表

6.为什么不直接用树

  • 短的时候 性能相似
  • 树结点保存的数据多 占空间

7.索引(桶下标)是怎么计算的

两次hash–> 用capacity取余–> 得出桶下标

取余的优化:

初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂

因为size一定为2的n次幂 所以说可以使用index = hash & (tab.length – 1)来取余做计算

image-20230221184522763

image-20230221185816185

容量是2的次幂的时候 性能较好,容量为质数时 hash分布较好(.net)。hashmap选择了2的次幂。二次hash也是保证分布性

8.sHashmap的put流程

image-20230222100221644

插入链表: JDK1.7版本及以前使用是头插法;JDK1.8使用的是尾插法

9.为什么1.8改成尾插了

image-20230222101143317

扩容时迁移 多线程下 一个线程已经迁移 另一个也迁移时 发生

ps:多线程下hashmap本来就不安全

10.为什么负载因子是0.75f

image-20230222100449866

11.hashcode里不能是可变对象

如果K是一个对象,hashcode中用到了对象属性,改变对象属性后,原来的K就失效了。

image-20230222102809818

12.String对象的hashCode()如何设计的,为啥每次乘的是31

image-20230222103316702

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap的put方法中,它需要经历初始化、存值、扩容、解决冲突等一系列操作。首先,在初始化时,会创建一个HashMap对象。然后,通过hash函数计算键的哈希值,这个哈希值将用于确定键值对在数组中存储的位置。具体来说,hash函数会根据键的hashCode方法返回的值进行计算。如果键为null,则哈希值为0;否则,哈希值等于键的哈希码与右移16位的结果进行异或运算。 接下来,在存值阶段,put方法会根据计算得到的哈希值找到键值对在数组中的位置。如果该位置为空,说明没有发生冲突,直接将键值对存储在该位置上。如果该位置已经存在其他键值对,可能发生了冲突。此时,HashMap采用链表或红黑树的方式来解决冲突。具体来说,如果链表的长度小于等于8,HashMap会使用链表来存储冲突的键值对;如果链表的长度超过8,HashMap会将链表转化为红黑树来存储键值对,以提高查找效率。 当存储的键值对数量达到一定阈值时,HashMap会进行扩容操作。扩容是为了减少哈希冲突,并提高HashMap的性能。扩容时,HashMap会创建一个更大的数组,并将原来数组中的键值对重新计算哈希值,然后存储到新的数组中。扩容过程中,需要重新计算每个键的哈希值,并重新确定存储位置。 综上所述,在HashMap的put方法中,会经历初始化、存值、扩容和解决冲突等一系列操作,以实现键值对的存储和查找功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [解析HashMap中的put方法](https://blog.csdn.net/qq_42453117/article/details/121904227)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [HashMap(一)——HashMap put方法原理](https://blog.csdn.net/the_one_and_only/article/details/81665098)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [HashMap put方法的源码分析](https://download.csdn.net/download/weixin_38729607/13751823)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值