在代码的海洋里,女码农们总是被贴上“硬核”、“理性”的标签,但你知道吗?
在深夜的键盘敲击声中,藏着多少不为人知的emo时刻?
接下来的这篇帖子,简直就是咱们程序员圈里的“emo大赏”,看得人心都揪成了一团乱麻。
想象一下,你正埋头苦干,代码改了一版又一版,领导却说:“感觉差点意思,再改改吧。”
这还不是最绝的,最绝的是他自己也说不清到底差哪儿!外包妹子直接泪崩,这哪是改代码啊,简直是改命啊!
甲方妹子那边也是,大领导一顿“爱的教育”,项目经理接着补刀,排期排得比高考还紧张,不哭都难!
一个需求扔过来,表在哪?字段啥意思?业务逻辑怎么绕?全靠自己摸索,领导还指望你秒懂。
这哪是工作啊,简直是玩“大家来找茬”的高级版,还TM是地狱难度!
但生活嘛,总得继续不是?
女码农们只能收起泪水,继续埋头苦干。
心里头那个“丧”啊,就像是一场永远不会停的“雨季”。
但咱们还是得笑中带泪地坚持,毕竟,“打工人,打工魂,打工都是人上人”(虽然这话听起来有点自我安慰的味道)。
说真的,技术圈这个地儿,有时候真的挺“丧”的。
大家都在焦虑中挣扎,却又找不到出口。
就像是一群“孤独患者”,在“代码雨林”里迷失了方向。
每次看到这样的帖子,都忍不住想抱抱自己,然后说一句:“原来你也在这里”。
。。。
言归正传。今天要分享的面试题是:
HashMap在JDK1.7和JDK1.8中的区别?
答:
通存储结构
- JDK 1.7 中的 HashMap 使用数组 + 链表的方式来存储元素。数组的每个元素即为一个桶,每个桶可能包含一个链表,多个元素通过链表连接在同一个桶中
- JDK 1.8 对 HashMap 进行了优化,引入了红黑树。当链表长度达到一定阈值(默认是8)时,链表会转换为红黑树。这是为了提高在哈希冲突较严重时的查询效率,将链表的线性查找优化为红黑树的对数查找。
哈希冲突解决方式
- jdk1.7中,当多个不同的键映射到相同的桶时,它们会形成一个链表。在查找键时,HashMap 遍历该链表,先hashcode后equals 方法判断是否为目标键。采用头插法插入数据。
JDK 1.8 对 HashMap 进行了优化,引入了红黑树。当链表长度达到一定阈值(默认是8)时,链表会转换为红黑树。这是为了提高在哈希冲突较严重时的查询效率,将链表的线性查找优化为红黑树的对数查找,O(logn)<O(n)。采用尾插法插入数据。
jdk1.8采用尾插法插入链表节点,原因在于
- 尾插法相比头插法在处理并发场景时,特别是多线程并发写入时,减少了对链表头部的竞争,提升了并发性能。这是为了更好地适应多核处理器的并发特性,减小了线程之间的竞争,提高了整体的性能表现。
CPU缓存对程序的性能至关重要。如果数据结构中的元素顺序与CPU缓存行对齐,可以大大提高缓存命中率,从而提高程序的执行效率。
扩容
- jdk1.7中当 HashMap 中的元素数量达到容量 * 负载因子(默认是0.75)时,会触发扩容操作。扩容操作会将桶的数量翻倍,并重新计算每个元素的桶位置。
扩容的方式相对于 JDK 1.7 有所改进。在 JDK 1.8 中,扩容不再采用逐个移动元素的方式,而是使用更高效的数组复制方法。
扩容时机
- putVal中判断数组的容量为空时,初始化数组table,初始化桶数组长度为16,阈值为16* 0.75 = 12。
- 当元素数量size达到阙值时即size > loadfactor * capacity 时,也是在putVal函数中,调用resize(),扩容后的数组大小是原数组的2倍,将原来的元素重新hash放入到新的散列表中去。
扩容过程
- 创建一个新的数组,其容量为旧数组的两倍,并重新计算旧数组中结点的存储位置。元素在新数组中的位置只有两种,原下标位置或原下标+旧数组的大小。
对key = null的处理
- jdk1.7中,hashmap会直接将null key放到index = 0的位置,遍历table[0]的Entry链表,寻找key的null的节点,找到后覆盖,未找到就添加一个null
- jdk1.8中,是先计算null的hash值的到0,然后和其他key一样用(n-1)& hash值计算得到index=0,然后,后续操作和他其他键一样。
- 注:hashtable中不支持key为null
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。