HashMap在jdk1.7和1.8之后的底层存储结构是不一样的
JDK 1.7 HashMap 的底层数据结构是 数组 + 链表
JDK 1.8 HashMap 的底层数据结构是 数组 + 链表/红黑树, 当链表的长度大于等于8时要转成红黑树
在jdk1.8后引入了红黑树
在学习过程中有以下几个关键问题需要弄清楚:
1 HashMap的初始数组容量是2的n次幂
在put值的时候通过当前key的hash值和数组长度求余得到一个索引位置,然后将数据放在该位置上,如果该位置不发生hash碰撞 那么放在该位置的第一个元素上,如果发生hash碰撞那么就放在该位置所在的链表上。
由于取模运算的效率远远低于位运算,在hashmap的底层源码中 计算索引的算法是 基于hash值 和 数组长度进行位运算:
hash & (length-1) 其中hash 就是通过hash散列函数计算出的hash值。
而只有length是2 的n次幂的时候 hash & (length-1) == hash % length 、
另外:因为在put值 或者扩容的时候都需要计算索引。
2 HashMap的加载因子是0.75
理想状态下 我们往put值的时候 优先将数据均匀的分散在数组上。而不是 有些数组上的链表过长。
如果加载因子是1 的时候:那么必须是数组上所有元素都存放了值后才进行扩容,那么就会出现更多的hash碰撞。会产生长的链表。因此需要用空间换时间,查询效率会低。
如果加载因子是0.5的时候:那么会产生更多空间。hash碰撞的可能性会低。而且链表的长度会越小。查询效率很高。因此在空间利用率和时间复杂度之间取均衡 取值为0.75
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
题外:
>>> 数组的查询效率 O(1) 链表的查询效率O(n)
>>> 红黑树接近于平衡查找二叉树 复杂度 O(logn)
>>> 每次put数据会影响红黑树的平衡,需要动态调整红黑树,也是需要消耗性能的。一般调整数的平衡方式就是左旋 和右旋 已经重新着色 维持平衡。