Java7/8 HashMap 底层结构分析 2021-07-23

HashMap
1、java8之前底层结构为:数组+链表.
– 可以理解为当你new出一个HashMap时,首先会初始化一个指定长度的数组,数组的长度默认为16,数组的每一个元素都是一个单向链表,链表中每一个节点都包含四个属性:key的hash值、key、value、next指针.基于这种结构,想要定位一个entry,首先要根据key的hashcode与数组长度取模,模数即数组下标,这样可以定位到某一个数组元素,该动作时间复杂度为O(1),再遍历该元素下的链表,时间复杂度为O(n).
2、Java8后底层结构为:数组+链表或红黑树.
– 主要区别:
— 当链表长度>8且数组长度<64,进行扩容;当链表长度>8且数组长度>=64,则转为红黑树;当红黑树中的节点个数<=6时,转为链表.结合了红黑树,使查询的时间复杂度降低为O(logn).
– 为什么链表转红黑树的阈值是8?
— 一方面是因为一个离散性比较好的hash算法,在同一个槽位产生8次哈希冲突的几率是千万分之6,是不太可能继续出现哈希冲突的.换句话说,如果真的发生了8次冲突,说明hash函数本身很可能是有问题的,那么下一次继续出现冲突的概率就很大,所以作为保底策略会转换为红黑树;再者链表长度过长,遍历耗时也会很严重,效率会下降,转树可以用空间换时间,提高性能.
– 为什么红黑树转链表的阈值是6?
— 如果该阈值也是8,那么在发生8次hash冲突时,会发生链表与红黑树不停相互转换,浪费资源;另外6也是综合空间与时间的结果,6个节点的链表占用的空间肯定比红黑树少,查询的效率也不会低太多.
– 为什么转树的第二个条件是数组长度>64?为什么>64不是继续扩容?
— 时间与空间平衡的结果.虽然扩容与树化都是空间换时间的思想,但是据我猜测:当数组长度超过64后,扩容付出的空间代价是高于树化的.这时候做树化处理,一方面提高了查询效率,另一方面节省了空间.但是树化是不会降低哈希冲突几率的.
– 红黑树特点?为什么选红黑树?
— 红黑树是一种平衡二叉树,它的左右子树高度尽量均匀,比二叉树效率更高,时间复杂度最大为O(log(n)).
3、put方法逻辑
– 大致逻辑:
— 先初始化数组,然后获取key.hashCode,再与数组长度取余,得到数组下标位置,如果该位置没有值,则放入;反之则从头遍历链表,逐个执行equals方法,如果相等则把value更新,返回旧value.;如果比较的结果全部为false,则放入该k-v.注意:Java7会将entry放入单向链表的头部,Java8会放入尾部.
– 1.7与1.8不同之处:
在这里插入图片描述
4、扩容逻辑:
– HashMap扩容时,其数组长度会扩展为>原长度2倍的最近的2的n次方的值.
– 扩容因子:
— 扩容因子越大,填满的数据越多,空间利用率越大;冲突概率越大,链表越长、查询效率越低.
— 扩容因子越小,冲突概率越小,链表越短,查询效率越高;填满的元素越少,空间利用率越低,扩容频率越高,耗费性能.
— 总结:扩容因子设置是一种时间与空间的平衡,设置小的因子,可以提高查询效率,但要考虑频繁扩容带来的性能消耗;设置大的因子,牺牲了查询效率,换取更大的使用空间.
5、并发死循环问题
– 本质:并发执行put操作导致触发扩容行为,从而导致环形链表,使得在获取数据遍历链表时形成死循环.
在这里插入图片描述
6、问题
– 哈希表如何解决哈希冲突?
— 避免:
好的hash算法:扰动处理:位运算、异或.
扩容机制:大于等于阈值则扩容.
— 解决:
数据结构:HashMap的链表、红黑树.
良好的数据存储机制:发生冲突时,采用链表+尾插法+红黑树.
– 为什么HashMap的k、v都允许为空?
— key为空时,hash值为0,只能有一个key为null.
– 为什么HashMap线程不安全?
— 未加同步锁.
— 1.7 并发执行put操作,导致扩容发生,导致环形链表,导致死循环.
— 迭代器的fail-fast策略:一旦在使用迭代器过程中出现并发操作,则抛出ConcurrentModificationException异常.
– 为什么无序?
— HashMap的存储顺序是根据hash算法计算而来的数组下标顺序,是随机的、均匀的、无规律的.
– 为什么存储位置随时间变化?
— 存在插入某个数据后触发扩容的情况,扩容后存储位置重新计算.
– 为什么String、Integer这样的包装类适合做key?
— final类型保证key的不可更改性.
— 内部已经实现了合适的equals()、hashCode()方法.
– HashMap中的key若为Object类型,需要实现哪些方法?
— equals():比较存储位置上是否存在需存储数据的key,存在则覆盖,反之插入.保证了key的唯一性.
— hashCode()方法:计算需存储数据的存储位置.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值