HashMap源码解读

本文深入解析HashMap的工作原理,包括hash算法、初始化容量、负载因子、扩容阀值、hash冲突处理、put与get方法,以及遍历方式。特别关注1.8版本中对链表到红黑树的优化,解决并发操作时可能出现的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hash算法

hash算法分为两部分,计算key的hash值,通过key的hash值来定位数据所在的位置。首先是计算key的hash值的方法。hash方法通过将key的hashCode的高16与低16位进行按位异或计算。目的是将高位的特征与低位的特征相结合,获取分布更加均匀的hash值,减少hash冲突的概率。

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

通过下面的公式来定位数据的位置。n为hashMap桶位的容量,计算位置的方法是hash%n,但是这里不是用的取模运算,而是用按位与运算替代。这两个公式相等需要有一个前提条件就是n为2的x次方。hash%n计算为hash除以n的余数,当n为2的x次方时,hash/n 等价于 hash >> x,那么右移出的x位则为hash/n的余数。得到此数据的一个方法是拿x位1与hash做按位与运算就可以计算出hash的右边x位。由于n=2x,所有n-1就为x位1。由此可得当n=2x时,(n-1)&hash = hash%n。

(n - 1) & hash

初始化容量

默认初始化容量为16,如果创建时传递了initialCapacity参数,通过tableSizeFor计算大于initialCapacity的最小的2的N次方。

static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

int类型二进制为32位,这里通过移位操作来将n的全部位转为1,然后通过n+1就得到了大于n的最小的2的N次方。先减1的目的是为了处理cap本身就为2的N次方的情况。

负载因子与扩容阀值

负载因子默认为0.75,扩容阀值为当前容量*负载因子。当达到扩容阀值时,通过resize方法将容量扩充为当前容量的2倍。比如容量为16当存储的数据数量超过12时,会自动扩容为32。

hash冲突时的处理

HashMap中处理hash冲突的方式为拉链法,当有冲突发生时,会将hash值相同的数据以一个单链表的形式存储在同一个位置。在1.8及以后对此中情况进行了优化,在链表的长度达到8时会将链表转为红黑树以提高查询数据的性能。

put方法

  1. 是否已经初始化数组,如果没有初始化,先通过resize方法进行初始化。
  2. 通过key的hash值计算对应位置,判读是否有元素存在,如果没有直接存入。
  3. 如果存在元素,判断是链表还是红黑树然后存入数据
  4. 判断是否达到了扩容阀值,如果达到了,进行扩容

get方法

  1. 通过key的hash值计算应在位置
  2. 不存在数据返回null
  3. 存在数据,判断是链表还是红黑树
  4. 遍历链表或者红黑树,比较key是否相同,相同返回数据,没有相同的返回null

遍历方式

  1. entrySet
  2. keySet
  3. values

这三种方式全部是通过内部类以HashMap数据的视图方式实现,直接使用HashMap中的存储结构,不需要额外的存储空间。最推荐的方式是entrySet方式遍历数据,因为跟HashMap存储数据的格式一致,直接获取key-value键值对,keySet需要先获取key然后再通过get方法来获取value。

并发情况下有什么问题

当多个线程同时操作一个HashMap时,碰到同时修改HashMap结构的操作时(添加或者删除键值对),会出现数据丢失或者死循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值