Java:hashmap

发布于 2021-06-19  726 次阅读


JDK1.8 Hashmap采用的是数组+链表+红黑树的数据结构

如果旧值的hash和旧的容量计算&为0,则扩容后的位置等于原来坐标。

如果旧值的hash和旧的容量计算&为1,则扩容后的位置等于原来坐标+旧的容量

扩展知识

  • JDK1.7和JDK1.8Hashmap区别?

  JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法。因为JDK1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。

   扩容后数据存储位置的计算方式也不一样。见第三点

  • 为什么负载因子不是0.5或1?

  如果是0.5,临界值是8 则很容易就触发扩容,而且还有一半容量还没用
  如果是1,当空间被占满时候才扩容,增加插入数据的时间
  0.75即3/4,capacity值是2的幂,相乘得到结果是整数

  • 为什么在JDK1.8中进行对HashMap优化的时候,把链表转化为红黑树的阈值是8,而不是7或者5呢?

根据注释中写到,理想情况下,在随机哈希码和默认大小调整阈值为 0.75 的情况下,存储桶中元素个数出现的频率遵循泊松分布,平均参数为 0.5,有关 k 值下,随机事件出现频率的计算公式为 (exp(-0.5) * pow(0.5, k) /factorial(k)))大体得到一个数值是8,那么退化树阀值为什么是6?如果退化树阀值也是8,则会陷入树化和退化的死循环中。如果退化阀值是7,假如对hash进行频繁的增删操作,同样会进入死循环中。如果退化树阀值小于5,我们知道红黑树在低元素查询效率并不比链表高,而且红黑树会存储很多索引,占有内存。所以退化阀值设为6比较合理。

  • JDK1.7是先扩容再插入,而JDK1.8是先插入再扩容。为什么?

   这个问题网上查找很多资料没有明确答案。可能原因是JDK1.7采用头插法,扩容后,计算hash,只需要插入链表头部就行。而JDK1.8采用尾插法,如果先扩容,扩容后需要遍历一遍,再找到尾部进行插入。


欢迎欢迎~热烈欢迎~