HashMap结构和底层原理

HashMap

HashMap主要由数组和链表组成,他不是线程安全的。核心的点就是put插入数据的过程,get查询数据以及扩容的方式。JDK1.7和1.8的主要区别在于头插和尾插方式的修改,头插容易导致HashMap链表死循环,并且1.8之后加入红黑树对性能有提升。

HashMap中put插入数据流程?

往map插入元素的时候首先通过对key hash然后与数组长度-1进行与运算((n-1)&hash),都是2的次幂所以等同于取模,但是位运算的效率更高。找到数组中的位置之后,如果数组中没有元素直接存入,反之则判断key是否相同,key相同就覆盖,否则就会插入到链表的尾部,如果链表的长度超过8,则会转换成红黑树,最后判断数组长度是否超过默认的长度*负载因子也就是12,超过则进行扩容。

HashMap如何定位到hash值在数组中的具体位置的?

数组长度-1进行与运算((n-1)&hash),跟hash % n 结果一样

HashMap中如何get查询数据?

查询数据相对来说就比较简单了,首先计算出hash值,然后去数组查询,是红黑树就去红黑树查,链表就遍历链表查询就可以了。

HashMap 什么时候进行扩容?

HashMap中的负载因子默认是0.75。比如当然容量大小是100,当存到76个的时候,判断发现需要进行resize了,那就进行扩容,但是HashMap的扩容也不是简单的扩大点容量这么简单的。

HashMap 是怎么扩容的呢?

分为两步
1.创建一个新的Entry空数组,长度是原数组的2倍。
2.遍历原Entry数组,把所有的Entry重新Hash到新数组。

为什么要重新Hash呢,直接复制过去不香么?

因为长度扩大以后,Hash的规则也随之改变。
原来长度(Length)是8你位运算出来的值是2 ,新的长度是16你位运算出来的值明显不一样了

为啥之前用头插法,java8之后改成尾插了呢?

举个例子,我们现在往一个容量大小为2的put两个值,负载因子是0.75是不是我们在put第二个的时候就会进行resize?
2*0.75 = 1 所以插入第二个就要resize了
使用头插会改变链表的上的顺序,但是如果使用尾插,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。

HashMap为啥会线程不安全?

1.jdk1.7中的HashMap
1.1 扩容造成死循环分析过程
1.2 扩容造成数据丢失分析过程
2.jdk1.8中HashMap

猜你喜欢

转载自blog.csdn.net/weixin_43851064/article/details/114690272