HashMap的数据结构(JDK1.8及之后)

HashMap的底层数据结构是 数组 + 链表 + 红黑树在这里插入图片描述
当我们new一个HashMap集合对象时,JVM不会立刻初始化一个哈希表,而是等到我们给集合对象添加第一个元素时,才会初始化哈希表(懒加载)。

存储一个元素时,是如何确定元素的存放位置呢?

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

在这里插入图片描述
从上面的源码,我们可以看出当put一个元素时,会将键key传递给hash()方法,hash()方法中将获取到的key哈希值进行异或和移位运算(扰乱函数),得到一个新的哈希值并返回,其目的是为了均匀分布。

新的哈希值传递进putVal()方法,与数组的长度值减一,进行与运算,以实现对哈希值映射到数组长度范围,获得元素存放的索引位置(路由寻址)。
假设新的哈希值为138,数组长度为16,则会将138 & (16-1)-> 10,则该元素在数组的索引为10。
在这里插入图片描述
如果计算得出的索引位置,已经存有元素(哈希冲突),那么以链表的形式尾插到已有的元素后面。

HashMap的初始长度是16,每次扩容时都会在原始的长度上翻倍(size × 2),所以长度一定是2的n次方。

红黑树的出现时机(链表树化):1. 链表的长度达到8; 2. 元素的总数量达到64。

红黑树退哈成链表: 红黑树中元素的数量小于6。

哈希桶扩容的条件:元素数量 >= 长度(16)× 加载因子(0.75)

扩容时,需重新计算桶中每一个位置的内容:

如果原桶内容为空,则直接复制过去;

当桶位置连有链表时,则计算出该链表的低位链和高位链,如果是低位链则直接复制到原索引位置,如果是高位链,则复制到(原索引 + 原长度)的位置(新索引),桶位置是红黑树时,同理,因为树种有退化成链表时需要的节点地址值,所以红黑树同样可计算出该树的低位链和高位链。

链表转换成红黑树的阈值为什么是 8 ,而加载因子为什么是0.75?

在hashCode离散性很好的情况下,红黑树用到的概率非常小,因为数据均匀分布在每个桶中,几乎不会有桶中链表长度会达到阈值(8)。但是在随机hashCode下,离散性可能会变差,然而JDK又不能阻止用户实现这种不好的hash算法,因此就可能导致不均匀的数据分布。
事实上,随机hashCode算法下所有桶中节点的分布频率遵循如下的泊松分布。在扩容阈值为0.75的情况下,(即使因为扩容而方差很大)遵循着参数平均为0.5的泊松分布。一个桶中链表长度达到8个元素的概率为0.00000006,几乎是不可能事件。之所以选择8,是时间和空间的权衡(trade-off),是根据概率统计决定的, 是非常严谨和科学的。

大部分情况下,链表存储能节约存储空间同时有着良好的查找性能;极个别情况下,节点数达到8个,转为红黑树,能获得更好的查找性能,同时因为是个别情况,不需要大量的存储空间。

1、TreeNodes(红黑树)占用空间是普通Nodes(链表)的两倍,为了时间和空间的权衡。
2、节点的分布频率会遵循泊松分布,链表长度达到8个元素的概率为0.00000006,几乎是不可能事件.

为什么转化为红黑树的阈值8和转化为链表的阈值6不一样?

为了避免频繁来回转化。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值