HashMap

1.为什么HashMap数组的长度得是2的N次方?

2的N次方对应的二进制(高位未补零)(2的N次方)-1 对应的二进制(高位未补零)
N=1101
N=210011
N=31000111
N=410000

1111

......

HashMap使用到的数据结构有数组、链表、红黑树。HashMap存储的键值对要存储在数组的哪个下标位置呢,是通过对2的N次方-1、key的hash值做与运算。拿N=4举例,那就是对1111和key的hash值做与运算,与运算的计算结果范围是[0000,1111],也就是十进制的[0,15],不会发生下标越界。

如果写代码时指定的长度不是2的N次方,例如,Map<String, Integer> map = new HashMap<>(13);那么HashMap实际创建数组的长度是大于13的2的N次方值,也就是16。

那为什么不用取余来计算下标值呢?例如数组长度16,key的hash值和15求余,这样算出来的值也都属于[0,15]这个范围,是因为与运算的效率高于求余运算。

从上面可知,当向map put一个键值对的时候,会计算要保存在哪一个下标位置,例如要存储在下标为i的位置。我们知道两个数据可能值不相同但是hash值相同,因此需要判断下标i的位置是否已有键值对存储在这里了。

1、数组下标i的位置没有存储数据,那么就直接保存在数组下标i这个位置。

2、数组下标i的位置已经存储数据了。

        1)判断当前存储的这个键值对和需要保存的键值对的key是否相同。

                a)相同,覆盖旧的value。

                b)不相同

                        判断是否节点是否是TreeNode

                                是:说明是红黑树结构 // todo,待补充

                                否:说明是链表结构,循环链表,当前键值对的key是否和链表中某个Node的key相同,如果相同,覆盖旧的value;如果和链表中所有Node的key都不相同,找到链表的尾节点,将当前键值对封装成Node插入链表尾部(尾插法)。如果不算当前节点,链表长度已经是8,算上当前节点,链表长度是9,就大于链表树化成红黑树的阈值8,那么就要把这个9个节点的链表改成红黑树了。这个过程是先遍历再添加再改成红黑树。

为什么要将链表改成红黑树呢?因为链表太长了的话,get和put都会遍历链表,效率低。

当链表长度=8时:

1、数组的长度<64,进行扩容(resize,数组长度扩大一倍),将一个链表拆分成两个短链表。

2、数组的长度>=64,不扩容,而是将链表改成红黑树。

将链表改成红黑树,会遍历链表,根据链表的Node,生成对应的TreeNode(是双向链表。和单向链表相比,删除更方便)。再将双向链表改成红黑树,首先将第一个节点作为红黑树的根节点,然后依次判断下一个节点应该处于红黑树的哪个位置(根据红黑树的特点)。然后将数组存储的链表节点的引用修改成红黑树节点的引用,链表就会被垃圾回收。

扩容就是把旧数组的所有节点搬到新数组的过程,新数组的长度是旧数组长度的2倍。

计算应该位于新数组的哪个下标位置呢?

计算方法仍然是对key的hash值和2的N次方-1做与运算(此时N的值是旧数组N的值+1)。

原因举例说明:

假如旧数组的长度是16,则新数组的长度是32。15对应的二进制是1111,31对应的二进制是11111。

旧的下标值 = key的hash值 & 01111。

新的下标值 = key的hash值 & 11111。

新下标值只有两种情况:

1、新下标值=旧下标值。(key的hash值红色对应位是0

2、新下标值=旧下标值+旧数组的长度。(key的hash值红色对应位是1

key的hash值红色对应位是0还是1呢?可以将key的hash值和16(本例中是16,对应二进制是10000)做与运算,如果运算结果是1,则红色对应位是1;如果运算结果是0,红色对应位是0。

数组存储的四种情况:

1、null,该下标没有存储元素。

2、存了一个键值对。

3、存的是一个链表。

4、存的是一个红黑树。

针对上面四种情况的搬家:

1、无需操作。

2、重新计算新的数组下标。

3、重新计算节点新的数组下标,链表中的节点新的下标值有两种情况(上面已经说明),因此就将这个一个链表拆分成了两个链表。将新数组的对应下标分别指向这两个链表。

4、重新计算节点新的数组下标。同理,新的下标值有两种情况。举例:如果搬家前红黑树共10个节点,7个节点要搬到新数组的高位,3个节点要搬家到新数组的低位,由于3<6(6是红黑树要退化成链表的阈值),搬到新数组的就是一个红黑树加一个链表。根据红黑树退化成链表的阈值可以知道,一个红黑树,搬过来有三种情况:1)两个链表。2)一个链表+一个红黑树。3)一个红黑树。

                        

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值