hashmap

71 阅读2分钟
存储结构?
  • JDK1.8 之前 数组+链表
  • JDK1.8几之后 数组+链表+红黑树
hashmap的长度为什么是2的N次幂?
  • 向hashMap中添加元素的时候,需要计算下标的值,hash%lengh = hash & (length - 1)的前提是length是2的N次幂,位运算比取模效率高,所以hashmap使用的是位运算
  • 均匀分布
为什么链表节点大于8才转换为红黑树?
  • 在hash算法满足柏松分布概率情况下,,hash碰撞超过8的概率很小
加载因子0.75 也就是默认12
  • 加载因子越小那么数据约稀疏,出现链表的概率也就越小,反之越大
  • 设置为0.75只是一个参考值
put方法的过程?
  • 计算桶的索引位置 hash & (length - 1)
  • 如果桶上没有碰撞直接插入
  • 如果有碰撞
    • 如果当前桶是红黑树则使用红黑树的插入方法
    • 如果当前是链表则使用链表插入方法,链表长度大于8 将其转换为红黑树
  • 如果桶中存在重复的key,则更新值
  • 如果size大于阈值则进行扩容
get方法过程?
  • 计算桶的索引位置 hash & (length - 1)
  • 桶上的key就是查询的结果
  • 桶上后续为链表,查询链表
  • 桶上后续为红黑树,查找红黑树
扩容?
  • 当hashmap的元素个数超过 capacity*loadfactory 则进行扩容为2N
  • 当hashmap的size没有达到64的时候,链表长度达到8个,hashmap会先扩容解决,反之链表转换为红黑树
  • 扩容时数据要么在原位置,要么在原位置 + 旧容量这个位置
为什么使用红黑树而不是用二叉树?
  • 二叉树极限情况下与链表没区别,红黑树牺牲插入的性能提高查询的性能
JDK1.8及之后为什么头插法更换为尾插法?
  • 尾插法是指在解决hash冲突时,将新的键值对插入到链表或者红黑树的末尾,而不是插入到链表或者红黑树的头部。
  • 多个闲层resize的场景下,尾插法能够防止出现死循环,在resize的时候尾插法的话不会改变原本链表元素的顺序,头插法改变了原本链表元素的指针

两个线程T1、T2准备同时进行扩容

截屏2024-04-04 21.20.23.png T1完成扩容

截屏2024-04-04 21.20.45.png 当线程T1执行完成之后,线程T2恢复执行时,死循环就发生了

截屏2024-04-04 21.21.10.png