超深度解析HashMap的关键底层逻辑

本文深入解析了Java中HashMap的get和put方法的源码,包括hash(key)方法的细节,如为什么使用^ (h >>> 16)来增加哈希值的随机性,以及为什么选择右移16位而非其他位数或不移动。此外,还介绍了getNode(int hash, Object key)方法中的索引计算原理。最后,阐述了HashMap的关键属性,如默认初始容量、加载因子、转为红黑树阈值等,及其对性能的影响。" 132009332,9354391,matlab实现总体最小二乘拟合点云平面,"['matlab', '线性代数', '计算机视觉', '算法', '开发语言']
摘要由CSDN通过智能技术生成

       

目录

1 解析get(Object key) 方法

1.1 源码展示

1.2 解析hash(key) 方法

1.2.1 解析(key == null) ? 0

1.2.2 解析(h = key.hashCode())

1.2.3 解析^ (h >>> 16)

(1)为什么^可以增加哈希值的随机性,从而减少哈希冲突的可能性

(2)为什么要h >>> 16?为什么是16位,左移操作可以吗,不移动可以吗?

        (3)为什么低位可能包含更多的随机噪声

1.3解析getNode(int hash, Object key)

2 解析 put(K key, V value) 方法

2.1 初始化变量

2.2 检查桶数组是否为空或长度为0

2.3 计算桶的索引并检查桶是否为空

2.4 处理哈希冲突

2.5 检查当前节点是否为红黑树的节点

2.6 遍历链表并处理插入

2.7 更新节点值

2.8 扩容和更新统计信息

3 属性解说

3.1 默认初始容量default_initial_capacity

3.2 默认加载因子default_load_factor

3.3 转为红黑树阈值treeify_threshold

3.4 退化为链表阈值untreeify_threshold

3.5 桶数组的最小容量min_treeify_capacity

3.6 修改次数modCount

3.7扩容阈值 threshold


        我主要从hashMap的源码层面,解析get函数、put函数这2个方法的核心逻辑,最后再说明下HashMap的关键属性的含义。

1 解析get(Object key) 方法

1.1 源码展示

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
}

后面重点剖析hash函数getNode函数

1.2 解析hash(key) 方法

hash(Object key)用来确定键值对在内部数组中的存储位置的关键算法,源码如下:
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

1.2.1 解析(key == null) ? 0

        这部分是一个三元运算符,用于检查传入的键(key)是否为null。如果key为null,则返回0。这是因为在Java中,任何对象与null比较都会返回false,而HashMap需要一个非负的哈希值来确定存储位置。如果key不为null,则执行后面的代码。

1.2.2 解析(h = key.hashCode())

        如果key不为null,则调用key的hashCode()方法来获取其哈希值,并将其赋值给变量h。hashCode()方法在Object类中定义,并被HashMap的键对象所继承。这个方法的目的是返回一个代表对象的哈希码值,通常是通过对象内部的一些数据计算得到的。

1.2.3 解析^ (h >>> 16)

        ^是按位异或运算符,它对两个二进制数进行按位异或操作。在这个上下文中,h >>> 16是将变量h的32位哈希值向右移动16位,这相当于对哈希值进行了一次高位的丢弃,保留了低16位的值。然后,这个结果与原始的哈希值h进行按位异或操作。

        按位异或操作的特点是,如果两个比特位相同,则结果为0;如果两个比特位不同,则结果为1。这种操作可以增加哈希值的随机性,从而减少哈希冲突的可能性。

(1)为什么^可以增加哈希值的随机性,从而减少哈希冲突的可能性
  1. 非线性变换:按位异或是一种非线性的位运算操作。当两个操作数的对应位不同的时候,结果为1;当对应位相同时,结果为0。这种非线性的特性使得异或操作能够将输入的位模式转换成一个看似随机的输出模式。这种转换有助于将输入数据中的局部相似性打散,从而使得输出的哈希值在位上看起来更加随机和分散。

  2. 位分布均匀:异或操作能够使输出的每一位都依赖于输入的多位,这样可以使得输出的每一位都有较高的熵(即不确定性)。当输入的哈希值经过异或操作后,每一位的值都与输入的多位有关联,这样可以使得每一位的输出结果更加均匀,减少了某些位可能聚集特定值的可能性。

  3. 减少连续相同位的可能性:在哈希值中,连续的相同位(如连续的0或1)可能导致哈希表中的冲突增加。异或操作通过改变位的值,可以打破这种连续性,从而减少连续相同位的可能性,有助于分散哈希值,减少冲突。

  4. 混合高位和低位信息:在代码(h = key.hashCode()) ^ (h >>> 16)中,h >>> 16是将哈希值的高16位移到低16位的位置,然后与原始的哈希值进行异或操作。这样做可以将高位的信息与低位的信息混合起来,增加了哈希值的整体随机性,有助于避免某些特定的高位模式导致的冲突。

  5. 对抗哈希碰撞攻击:在某些情况下,攻击者可能会尝试构造特定的输入数据,使得它们的哈希值相同,从而引发哈希碰撞。通过使用异或操作,可以在一定程度上对抗这种攻击,因为它增加了攻击者预测和控制哈希值输出的难度。

(2)为什么要h >>> 16?为什么是16位,左移操作可以吗,不移动可以吗?

        在Java的HashMap中,对哈希值进行右移操作(h >>> 16)而不是左移(h << 16),以及选择移动16位而不是其他位数,都是基于特定的设计考虑和优化策略。
        1. 右移 vs 左移:

        右移操作(>>>)与左移操作(<<)在位运算中有不同的效果。左移操作将所有位向左移动,右边填充0,这实际上是放大了数值的低位部分,而高位则被丢弃。右移操作则是将所有位向右移动,左边填充符号位(对于有符号整数),这样做保留了数值的高位部分,但丢弃了低位部分。

        在HashMap中,右移操作有助于保留原始哈希值的高位信息,同时通过丢弃低位信息来增加哈希值的随机性。这是因为高位通常包含更多的信息,而低位可能包含更多的随机噪声。通过这种方式,可以在一定程度上减少哈希冲突,因为高位信息对于区分不同的键更为重要。

        2. 移动16位 vs 移动其他位数:

        选择移动16位而不是其他位数是基于性能和效果的平衡。移动16位意味着原始哈希值的高16位和低16位被交换,这样可以更好地混合哈希值的高位和低位信息,增加随机性,从而减少哈希冲突的可能性。

        如果选择移动的位数太少,比如8位,那么混合的效果可能不明显,高位和低位的信息可能仍然保持较大的独立性,这可能导致哈希值的分布不够均匀。如果移动的位数太多,比如24位,那么保留的信息就会更少,这可能会导致哈希值的随机性降低&#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云潭键神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值