HashMap 底层存储原理

1. put方法 -- JDK1.7的头插法

HashMap 底层存储原理_第1张图片

2. hash算法

3. findVal(K, Entry entry)

HashMap 底层存储原理_第2张图片

 

JDK 1.8  HashMap 

1. hashmap中存储数据的结构?

jdk8中hashmap 数组 + 链表 + 红黑树。每一个数据单元都是一个Node结构,Node中包含 key字段、value字段、next字段、hash字段。

2. hsahmap散列表数组初始长度

默认是16。散列表不是 new HashMap()时创建的,散列表是懒加载机制,只有第一次put数据的时候才创建。

3. 默认的加载因子

0.75。负载因子的作用:计算扩容阈值用的。

例如:使用无参构造方法创建的hashmap对象,它默认情况下扩容阈值是:16*0.75 = 12 。

4. 链表转化为红黑树需要达到什么条件?

主要是有两个指标:1)是链表长度达到8.

2)是当前散列表数组长度已经达到64.否则,就算内部链表长度达到8了,也不会发生链转树;而是仅仅发生一次resize,散列表扩容。

5. Node对象内部有一个hash字段,这个hash字段的值是key对象的hashCode()返回值吗?

不是。这个hash值是key的hashCode()二次加工得到的·。高16位异或低16位

高16位异或低16位,主要解决均匀分布的问题。

寻址算法:hash & (table.length - 1)

6. hashmap的put方法流程

4种情况?

寻址算法都一样:都是根据 key的hashCode 经过高低位异或之后的值,然后再按位与 & (table.length - 1),得到一个槽位(slot)下标。

这个下标,槽内状况不同,情况也不同。四种状态。

1)slot == null :直接put占用这个slot(槽)。把当前方法传进来的key和value封装成一个node对象,放到这个slot中。

2) slot != null,并且它引用的Node 还没有链化。

这种情况,需要先对比一下,Node对象的key与当前put对象的key是否完全相等。若相等,这就是replace操作,替换value就可以。

若不相等,这个put操作就是hash冲突,在slot->node 后面插入一个新的节点,采用尾插法。

3)slot != null && slot内部的node已经链化。

与2)类似。迭代查找node,看链表上元素的key,与当前传来的key是否完全一致。一致的话,replace.

put之后,还需要检查当前链表的长度,有没有达到树化阈值。若达到阈值,就调用一个树化方法,具体的树化操作都在这个树化方法里面完成。

4)冲突很严重的情况,链已经转化为红黑树。
红黑树的写入操作

 

7. hashmap的扩容机制

7.1 什么情况下会触发扩容

hashmap结构内 记录当前数据量的字段。这个数据量的字段达到扩容阈值,就会触发扩容操作。

7.2 扩容的规则

首先,table的数组长度必须是2的次方:

当数组长度不为2的n次幂 的时候,hashCode 值与数组长度减一做与运算 的时候,会出现重复的数据。因为不为2的n次幂 的话,对应的二进制数肯定有一位为0 , 这样不管你的hashCode 值对应的该位,是0还是1 ,最终得到的该位上的数肯定是0,这带来的问题就是HashMap上的数组元素分布不均匀,而数组上的某些位置,永远也用不到。

扩容每次都是按照上一次tableSize位移运算得到的,就是左移 1 位运算。

假设,tableSize = 16, 16 << 1 = 32.

7.3 为什么采用位移运算,不直接tableSize乘以2呢?

主要是因为性能,CPU不支持乘法运算,乘法运算都转化为加法指令实现,效率很低。位运算对cpu来说高效。

7.4 原来老数组中的数据怎么迁移呢?

 

 

 

 

你可能感兴趣的:(JavaSE基础)