Map (四) HashMap 已故事的角度理解 resize()

我们先来看一篇故事,有益我们有更好的理解。

很久以前有一个学校,有7500名学生(size)和100间教室(oldCap)。学生的学号为0000~7499(hash),教室的编号为0~99 (数组下标)。为了使学生均匀的进入每间教室,校长规定,按学生学号的末尾2位选择对应编号的教室(hash & (数组长度 - 1)),如:

  • 学号 0001 进入 01 教室
  • 学号 0012 进入 12 教室
  • 学号 0212 进入 12 教室
  • 学号 7001 进入 01 教室
  • 学号 7401 进入 01 教室

学校规定:如果学生超过7500名(超过 oldThr),就将教室扩充,扩充 10 倍,即1000间(newCap)。将1000间分为10个区(原来的区对应低位,多出来的9个区对应高位)

(每个区的每个房间教室摆放一摸一样,只有编号不一样)

  • 0区 0~99
  • 1区 100~199
  • 2区 200~299
  • 以此类推

(因为 HashMap 计算是2进制,所以扩充2倍 , 而我们这里计算为 10 进制,所以扩充10倍。为了更方便理解。)

不巧过几天就来了一名新学生,校长也如约的将教室扩充到了1000间。扩充完毕后就需要重新分配位置了,这次的规定是,按学生学号的末尾3位选择对应编号的教室,如:

  • 学号 0001 进入 001 教室
  • 学号 0012 进入 012 教室
  • 学号 0212 进入 212 教室
  • 学号 7001 进入 001 教室
  • 学号 7401 进入 401 教室

对于教室扩充为1000间,并将匹配的学号尾数由2位变成了3位。可以看到,学号第二位(从左往右数)为0的学生,教室位置根本就没有变化。

所以校长为了加快换教室的速度,指挥到:“学号第二位是0的学生坐在原位不要动((e.hash & oldCap) == 0),不为0的学生((e.hash & oldCap) != 0),根据第二位的数字找到相应的区,同样位置的教室坐下!"。此话一出,学生快速找到了自己对映的区和对映的教室,不一会就各自在自己的位置上坐好了。

上面的故事抽象的描述了一下 HashMap 扩容时候的操作。最主要的就是在校长指挥的时候,判断学号第二位是否为0,为0就在原地不动,不为零加上相应的固定值(上面故事中固定值是自己学号的第二位固定值)。

所以要理解 HashMap 如何扩容,必须深刻的理解 HashMap 是如果通过 HashCode 进行计算得出存储位置的。


下面我们再来看图,看看是如何进行扩容的。

我们要先知道16的二进制是 00000…0010000.和以下几个需要明白的地方

  1. 一个16容量的 HashMap 数组,阀值是12,在存入第13个的时候开始扩容。
  2. 需注意 HashCode 是 32 位的字节码,但是在存储的时候其实一般关注后面的位数
  3. 黑色 bit 位 表示本次计算存储位置时需要用到的 bit 位,
    红色 bit 位 的表示本次计算存储位置时不需要考虑是什么值的 bit 位
  4. 每个链表的元素都分为两种颜色,一种深色,一种浅色。
    4.1. 深色元素代表本次扩容时,容量值(16)的二进制是1的坐标(16的2进制为1的,坐标是第5位)和 hash 相应坐标的值相同 (第5位为1)
    4.2. 浅色则和 hash 相应坐标的值不同(第5位为0)

在这里插入图片描述

可以看到,扩容后,重新计算位置时,同一个链表中,浅色元素全部留在了原来的位置,
深色元素全部都到了 (当前坐标 + 原来的的容量值)的位置。即:

  • 原来在 0 的去 0 + 16 = 16
  • 原来在 3 的去 3 + 16 = 18
  • 原来在 15 的去 15 + 16 = 31

这个就是 HashMap 扩充的特性。在相同链表的扩充后存储位置只有两种选择,要不留在原地,要不加上相应的原容量值。这也是 HashMap 扩容时最重要的环节。也是最耗时的地方(但是对于别的解决办法来说是最快的最高效的)。


最后附上流程图,看不清楚可以右键点击放大观看。
在这里插入图片描述
如果有那些讲的不好或者讲的不仔细的地方,欢迎评论留言。


Map (一) HashMap 构造函数的秘密
Map (二) HashMap put()方法详细解刨
Map (三) HashMap 如何利用 hash 计算存储位置
Map (四) HashMap 已故事的角度理解 resize()
Map (五) HashMap get()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值