面试阿里,HashMap 这一篇就够了(1),2024年最新javazookeeper面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

二狗:为什么不是 0.74 或 0.76?

囧辉:

二狗:那我们换个问题问吧,HashMap 的插入流程是怎么样的?

囧辉:Talk is cheap. Show you the picture。

二狗:图里刚开始有个计算 key 的 hash 值,是怎么设计的?

囧辉:拿到 key 的 hashCode,并将 hashCode 的高16位和 hashCode 进行异或(XOR)运算,得到最终的 hash 值。

static final int hash(Object key) {

int h;

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

}

二狗:为什么要将 hashCode 的高16位参与运算?

囧辉:主要是为了在 table 的长度较小的时候,让高位也参与运算,并且不会有太大的开销。

例如下图,如果不加入高位运算,由于 n - 1 是 0000 0111,所以结果只取决于 hash 值的低3位,无论高位怎么变化,结果都是一样的。

如果我们将高位参与运算,则索引计算结果就不会仅取决于低位。

二狗:扩容(resize)流程介绍下?

囧辉:

二狗:红黑树和链表都是通过 e.hash & oldCap == 0 来定位在新表的索引位置,这是为什么?

囧辉:请看对下面的例子。

扩容前 table 的容量为16,a 节点和 b 节点在扩容前处于同一索引位置。

扩容后,table 长度为32,新表的 n - 1 只比老表的 n - 1 在高位多了一个1(图中标红)。

因为 2 个节点在老表是同一个索引位置,因此计算新表的索引位置时,只取决于新表在高位多出来的这一位(图中标红),而这一位的值刚好等于 oldCap。

因为只取决于这一位,所以只会存在两种情况:1)  (e.hash & oldCap) == 0 ,则新表索引位置为“原索引位置” ;2)(e.hash & oldCap) != 0,则新表索引位置为“原索引 + oldCap 位置”。

二狗:HashMap 是线程安全的吗?

囧辉:不是。HashMap 在并发下存在数据覆盖、遍历的同时进行修改会抛出 ConcurrentModificationException 异常等问题,JDK 1.8 之前还存在死循环问题。

二狗:介绍一下死循环问题?

囧辉:导致死循环的根本原因是 JDK 1.7 扩容采用的是“头插法”,会导致同一索引位置的节点在扩容后顺序反掉。而 JDK 1.8 之后采用的是“尾插法”,扩容后节点顺序不会反掉,不存在死循环问题。

JDK 1.7.0 的扩容代码如下,用例子来看会好理解点。

void transfer(Entry[] newTable) {

Entry[] src = table;

int newCapacity = newTable.length;

for (int j = 0; j < src.length; j++) {

Entry<K,V> e = src[j];

if (e != null) {

src[j] = null;

do {

Entry<K,V> next = e.next;

int i = indexFor(e.hash, newCapacity);

e.next = newTable[i];

newTable[i] = e;

e = next;

} while (e != null);

}

}

}

PS:这个流程较难理解,建议对着代码自己模拟走一遍。

例子:我们有1个容量为2的 HashMap,loadFactor=0.75,此时线程1和线程2 同时往该 HashMap 插入一个数据,都触发了扩容流程,接着有以下流程。

1)在2个线程都插入节点,触发扩容流程之前,此时的结构如下图。

2)线程1进行扩容,执行到代码:Entry<K,V> next = e.next 后被调度挂起,此时的结构如下图。

3)线程1被挂起后,线程2进入扩容流程,并走完整个扩容流程,此时的结构如下图。

由于两个线程操作的是同一个 table,所以该图又可以画成如下图。

4)线程1恢复后,继续走完第一次的循环流程,此时的结构如下图。

5)线程1继续走完第二次循环,此时的结构如下图。

6)线程1继续执行第三次循环,执行到 e.next = newTable[i] 时形成环,执行完第三次循环的结构如下图。

如果此时线程1调用 map.get(11) ,悲剧就出现了——Infinite Loop。

二狗:(尼玛,没听懂,尴尬了)那总结下 JDK 1.8 主要进行了哪些优化?

囧辉:JDK 1.8 的主要优化刚才我们都聊过了,主要有以下几点:

1)底层数据结构从“数组+链表”改成“数组+链表+红黑树”,主要是优化了 hash 冲突较严重时,链表过长的查找性能:O(n) -> O(logn)。

2)计算 table 初始容量的方式发生了改变,老的方式是从1开始不断向左进行移位运算,直到找到大于等于入参容量的值;新的方式则是通过“5个移位+或等于运算”来计算。

// JDK 1.7.0

public HashMap(int initialCapacity, float loadFactor) {

// 省略

// Find a power of 2 >= initialCapacity

int capacity = 1;

while (capacity < initialCapacity)

capacity <<= 1;

// … 省略

}

// JDK 1.8.0_191

static final int tableSizeFor(int cap) {

int n = cap - 1;

n |= n >>> 1;

n |= n >>> 2;

n |= n >>> 4;

n |= n >>> 8;

n |= n >>> 16;

return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

}

3)优化了 hash 值的计算方式,老的通过一顿瞎JB操作,新的只是简单的让高16位参与了运算。

// JDK 1.7.0

static int hash(int h) {

h ^= (h >>> 20) ^ (h >>> 12);

return h ^ (h >>> 7) ^ (h >>> 4);

}

// JDK 1.8.0_191

static final int hash(Object key) {

int h;

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

}

4)扩容时插入方式从“头插法”改成“尾插法”,避免了并发下的死循环。

5)扩容时计算节点在新表的索引位置方式从“h & (length-1)”改成“hash & oldCap”,性能可能提升不大,但设计更巧妙、更优雅。

二狗:除了 HashMap,还用过哪些 Map,在使用时怎么选择?

囧辉:

二狗:(不妙,这个 B HashMap 懂得比我还多,得赶紧溜)到时间和女朋友吃饭了,我们之后再分胜负。

囧辉:

越努力,越幸运。转发【朋友圈】【收藏】【点赞】,是对囧辉最大的支持。

推荐阅读

====

Java 基础高频面试题(2021年最新版)

921天,咸鱼到阿里的修仙之路

两年Java开发工作经验面试总结

4 年 Java 经验,阿里网易拼多多面试总结、心得体会

5 年 Java 经验,字节、美团、快手核心部门面试总结(真题解析)

复习2个月拿下美团offer,我都做了些啥

最后

无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。

针对以上面试技术点,我在这里也做一些分享,希望能更好的帮助到大家。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
这家公司,是不是能真的得到锻炼。

针对以上面试技术点,我在这里也做一些分享,希望能更好的帮助到大家。

[外链图片转存中…(img-aOWqE5KJ-1713190377699)]

[外链图片转存中…(img-7w7uiVtu-1713190377700)]

[外链图片转存中…(img-WRfB1fFr-1713190377700)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-vwUfjVGQ-1713190377701)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值