深入Java集合学习系列:HashMap的实现原理(2)

本文详细介绍了HashMap的构造器及其性能参数,包括初始容量、负载因子的概念及其对性能的影响,并探讨了HashMap的Fail-Fast机制,帮助读者深入理解HashMap的工作原理。

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

5.    HashMap 的性能参数:

    HashMap  包含如下几个构造器:

    HashMap() :构建一个初始容量为   16 ,负载因子为   0.75    HashMap

    HashMap(int initialCapacity) :构建一个初始容量为   initialCapacity ,负载因子为   0.75    HashMap

    HashMap(int initialCapacity, float loadFactor) :以指定初始容量、指定的负载因子创建一个   HashMap

    HashMap 的基础构造器 HashMap(int initialCapacity, float loadFactor) 带有两个参数,它们是初始容量 initialCapacity 和加载因子 loadFactor

    initialCapacity HashMap 的最大容量,即为底层数组的长度。

    loadFactor :负载因子 loadFactor 定义为:散列表的实际元素数目 (n)/  散列表的容量 (m)

    负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是 O(1+a) ,因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

    HashMap 的实现中,通过 threshold 字段来判断 HashMap 的最大容量:

threshold = (int)(capacity * loadFactor);

结合负载因子的定义公式可知, threshold 就是在此 loadFactor capacity 对应下允许的最大元素数目,超过这个数目就重新 resize ,以降低实际的负载因子。默认的的负载因子 0.75 是对空间和时间效率的一个平衡选择。当容量超出此最大容量时,   resize 后的 HashMap 容量是容量的两倍:

if (size++ >= threshold)   
resize(2 * table.length); 

6.    Fail-Fast 机制:

    我们知道 java.util.HashMap 不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了 map ,那么将抛出 ConcurrentModificationException ,这就是所谓 fail-fast 策略。

    这一策略在源码中的实现是通过 modCount 域, modCount 顾名思义就是修改次数,对 HashMap 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount

HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}

在迭代过程中,判断 modCount expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 Map:

   注意到 modCount 声明为 volatile ,保证线程之间修改的可见性。

final Entry<K,V> nextEntry() {   
if (modCount != expectedModCount)   
throw new ConcurrentModificationException();

HashMap API 中指出:

    由所有 HashMap 类的 “collection  视图方法 所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的   remove  方法,其他任何时间任何方式的修改,迭代器都将抛出   ConcurrentModificationException 。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。

    注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException 。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

参考资料:

JDK API HashMap      HashMap  源代码      深入理解HashMap

通过分析 JDK 源代码研究 Hash 存储机制      java.util.HashMap源码要点浅析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值