HashMap扩展:扩展两倍,底层是数组,当数组填充时自动扩展。
扩展:增加数组长度,对原始数组执行刷新操作,并将原始数组复制到新数组中
问题:假设16个数组在[]的同一位置包含多个密钥值对。 扩展到32位时,某个对或多个对的key-value位置发生了更改。
扩容后,要进行rehash重新将hash值与n-1进行与运算如下
排列长度=16
n-10000000000000000000000000000000000000000111
hash 111111111111111111100001110000101
结果00000000000000000000000000000000000000000000001
结果=5(索引为5的位置)
n-10000000000000000000000000000000000000000111
hash 211111111111111111000011100010101
结果00000000000000000000000000000000000000000000001
结果=5(索引为5的位置)
如果数组长度为16,则两个散列值的位置相同,并在链表中处理,从而产生散列冲突问题
数组长度为32,http://www.Sina.com /
排列长度=32
n-10000000000000000000000000000000000000000111
hash 111111111111111111100001110000101
结果00000000000000000000000000000000000000000000001
结果=5(索引为5的位置)
n-10000000000000000000000000000000000000000111
hash 211111111111111111000011100010101
结果000000000000000000000000000000000000000000000101
结果21 (索引为21的位置)
判断二进制结果中是否多1个bit,如果没有则为原始索引,如果多则为索引old cap。 这样,对于rehash,可以对每个hash取新数组. length的类型,避免取类型性能不高、位运算性能高。
rehash实际上是去把原始数组的长度加到原始索引的位置。 如上所示,5 16=21
【评论区】
重新对每个hash值进行寻址,也就是用每个hash值跟新数组的length-1进行与操作
可以减少冲突概率,2的倍数-1得到的值保证所有比特都是1,计算值相和后结果都是单一的,比特上的0越多冲突概率越高。
例如,容量为2的4次方减去1等于1111。 那么,计算值1110与他相来,结果为1110,另一计算值1111与他相和结果仍然为1111,各自的结果不同且不冲突。 如果容量是2的4次方,例如15减去1,就是1110。 计算值1110来了,与他相加相,结果为1110,另一个计算值1111和他相与结果冲突,如在11110之前一样
1.面试题:为什么数组容量会是2的倍数,以及扩容为什么是扩成两倍?
容量*负载因子的默认长度为16个hashmap,负载因子的默认值为0.75,因此达到12时容量会扩大。 12表示HashMap中元素的总数(即size ) )方法的返回值
2.面试题:扩容和负载因子之间的关系,元素达到多少扩容2倍,怎么算的?
查看jdk源代码时,1.7是页眉,1.8是页脚。 页眉的插入会引起死循环问题,导致100%的CPU
3.如果是hash碰撞,后加入的值是加入在链表头还是链表尾呢?
水桶单元中只有一个数据时:直接重新计算e.hash(newcap-1 )新水桶的位置
如果此时段单元是链表,则根据(e.hash oldCap )==0旧时段的容量,确定新时段中的位置是原始位置还是原始位置。 链表的顺序不变。 分为两部分,一部分位于新铲斗中原位置,一部分位于新铲斗中原位置。 可以看到,这里并不是避免了对齐和运算,而是避免了链表
如果时段单元是红黑树,(e.hash bit )==0类似于时段单元是链表逻辑,它确定新时段中的位置是原始位置还是原始位置。 红黑树的顺序不变。 分为两个部分,一个位于新铲斗中原位置,另一个位于新铲斗中原位置。 同时将根节点放在桶单元上,判断树中数据长度的原理一致,转换为链表的判断变多了。