HashMap的底层结构
从jdk1.8之后,hashmap底层采用【数组+链表+红黑树】的结构,1.8之前是【数组+链表】
hashmap底层结构:
hashmap底层为啥要使用数组+链表?
使用 “数组+链表” 是为了解决 hash 冲突的问题
。
数组和链表有如下特点:
数组:查找容易,通过
index
快速定位;插入和删除困难,需要移动插入和删除位置之后的节点;
链表:查找困难,需要从头结点或尾节点开始遍历,直到寻找到目标节点;插入和删除容易,只需修改目标节点前后节点的next 或
prev
属性即可;
HashMap巧妙的将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做 “
拉链法
”
的方式来解决哈希冲突。
首先通过index 快速定位到索引位置,这边利用了数组的优点;然后遍历链表找到节点,进行节点的新增
/
修改
/
删除操作,这边利用了链表的优点。
1.8之后咋又引入红黑树呢?
引入红黑树是优化查找性能
通过上面可以看出,
“
数组
+
链表
”
已经充分发挥了这两种数据结构的优点,是个很不错的组合了。
但是这种组合仍然存在问题,就是在定位到索引位置后,需要先遍历链表找到节点,这个地方如果链表很长的话,
也就是
hash
冲突很严重的时候,会有查找性能问题,因此在
JDK1.8
中,通过引入红黑树,来优化这个问题。
使用链表的查找性能是 O(n),而使用红黑树是 O(logn)
。
链表和红黑树的使用时机
对于插入,默认情况下是使用链表节点。当同一个索引位置的节点在新增后超过
8
个(阈值
8
):如果此时数组长度大于等于 64
,则会触发链表节点转红黑树节点(
treeifyBin
);
而如果数组长度小于
64
,则不会触发链表转红黑树,而是会进行扩容,因为此时的数据量还比较小。
对于移除,当同一个索引位置的节点在移除后达到
6
个(阈值
6
),并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点(untreeify
)。
hashmap的重要属性
为什么初始容量是16,扩容必须是2的n次幂呢?
为了加快哈希计算以及减少哈希冲突
当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。
![](https://img-blog.csdnimg.cn/20210514092731338.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0OTU4MzI2,size_16,color_FFFFFF,t_70)
hashmap的插入流程