Jdk1.8之前HashMap中存储数据结构
HashMap的高效率
HashMap是将key做hash算法,然后将hash值映射到内存地址,直接获取key所对应的数据。HashMap中数据存储的结构是数组+链表,链表是为了解决hash碰撞问题。HashMap为什么快?主要是以下三点:
- hash算法是高效的;
- hash值到内存地址(数组索引)的算法是快速的;
- 根据内存地址(数组索引)可以直接获取对应的数据。
负载因子和阀值
负载因子又叫填充比,是一个介于0和1之间的浮点数,他决定了HashMap在扩容之前内部数组的填充度。默认HashMap初始大小16,负载因子0.75;
负载因子=元素个数/内部数组总数
Jdk1.8HashMap中存储数据结构的调整
在Jdk1.8以及以后的版本中,HashMap的底层数据结构由原来的数组+链表,调整为数组+链表/红黑树。结合源码分析一下:
首先明确链表的时间复杂度是O(n),红黑树时间复杂度是O(Logn)。既然红黑树的复杂度优于链表,那么为为什么HashMap不直接使用红黑树代替链表呢?
进入HashMap源码可以看到这段描述:
1 | * Because TreeNodes are about twice the size of regular nodes, we |
大概意思是说树的节点占的空间是普通节点的两倍,在节点足够多的时候才会使用树形数据结构,如果节点变少了还是会变回普通节点。总的来说就是节点太少的时候没必要转换、不仅转换后的数据结构占空间而且转换也需要花费时间。
小括号中有个see TREEIFY_THRESHOLD. 这里让提示上文说的足够大是多少:
1 | In |
描述信息为了使hashCode分布良好,树形结构很少使用。并且在理想状态下受随机分布的hashCode影响,链表中的节点遵循泊松分布,据统计链表中的节点数是8的概率已经接近千分之一且此时链表的性能已经很差。所以在这种比较罕见的和极端的情况下才会把链表转变为红黑树。
就是说大部分情况下HashMap还是使用链表,如果理想的均匀分布节点数不到8就已经自动扩容了。源码如下:
1 | /** |