目录
HashMap1.7 扩容机制
HashMap1.7 扩容机制:当 put 一个新键值对时;发生了哈希碰撞(hash值相同)并且加上新键值对后,键值对总数达到了阈值(容量 * 0.75)
① 如:容量为 16 的HashMap,每个位置都放了一个键值对;它并不会扩容,因为没有发生哈希碰撞
② 提问:容量为 16 的HashMap,最多可以放多少个键值对?
答:26个
在 0 号位置先加入 11 个键值对(其他位置都可以),然后将剩余空位置填满;结果为 11+15=26;假如再 put 一个新键值对,这时才会发生哈希冲突并且总数达到阈值。
扩容时产生死链
原因
① HashMap1.7 添加元素为头插法
② HashMap 线程不安全,在并发情况下触发了扩容
产生过程
场景:假设有一个已经达到阈值并且再添加一个新键值对就会发生哈希碰撞(触发扩容)的 HashMap,现在有两个线程往里添加新键值对,此时可能会出现死链
如图:
因为 1.7 为头插法;所以下标为 1 的位置,顺序与插入顺序相反。
假设此时:
Thread0 插入 key 的 hash 为 21 的键值对(扩容前在 5,扩容后会放到位置 21)
Thread1 插入 key 的 hash 为 22 的键值对(扩容前在 6,扩容后会放到位置 22)
两个线程的操作都会使 HashMap 扩容,并且假设 HashMap 在 Thread0 时扩容完成。
此时 1,33 还是会在原来的下标位置,转移到新数组执行的操作为:
第一次循环:
e 1
next 33
e.next null
newTable[1] 1 -> null
第二次循环
e 33
next null
e.next 1
newTable[1] 33 -> 1 -> null
Thread0: 33 -> 1 -> null
假设此时 Thread1 已经获取到 e 与 next 的值 并且刚开始迁移
第一次循环(e 与 next 已经发生改变):
e 1 -> null
next 33 -> 1 -> null
e.next null
newTable[1] 1 -> null
第二次循环
e 33
next 1
e.next 1
newTable[1] 33 -> 1 -> null
第三次循环
e 1
next null
e.next 33 -> 1 -> null
newTable[1] 1 -> 33 -> 1 -> null
由此产生了死链