HashMap负载因子对性能的影响

负载因子是HashMap扩容的指标,当键值对数量超过bins总量的0.75时,HashMap会扩容到原来的两倍。负载因子越小,碰撞概率降低但空间利用率下降;越大则碰撞概率增加。默认0.75是考虑到冲突概率和转换为红黑树的平衡。初始化容量和负载因子的选择影响HashMap的迭代和查找性能。

负载因子 ( loadFactor)

负载因子是扩充 table 的指标量, 默认 0.75, 当 Map中键值对数量 / bins总量 > loadFactor 时, 扩容 table 到原来容量的两倍.

负载因子的意义: 负载因子是在碰撞概率和空间利用率之间进行权衡的调节器. 从直观上感觉, 负载因子描述了 table 的稀疏程度.

  • 负载因子越小, 出现 hash 碰撞的概率越低, bin 中的链表越短; 同时空间利用率低 (空 bin 多, 浪费). Table 看上去非常稀疏.
  • 负载因子越大, 出现 hash 碰撞的概率越高, bin 中的链表越长; 同时空间利率也高 (空 bin 少, 不浪费). Table 看上去非常稠密.

看图从直观上感觉 table 的稀疏程度:

image-20220409183004294

左侧 table 加载因子 = 0.1, 右侧 table 加载因子 = 0.6. 不过实际上 bins 的分布不会这么均匀.

例如:

  • 场景一: table 长度 = 10, 负载因子 = 0.1, 每加入一个元素就会扩容, 浪费了空间, 但一定不会出现碰撞.

  • 场景二: 如果将场景一中的负载因子修改为1, 则加入 10 个元素后才会触发扩容, 可想而知因为这 10 个元素是通过 hash % size 落入 table 中的, 最理想的状态是每个 bulk 中落入一个元素, 但几乎可以肯定的是, 有的 bulk 中落入了两个或更多元素 (形成链表), 即出现了碰撞.

默认值为什么是 0.75?

0.75 是权衡的结果, 当加载因子为 0.75 时, bin 中增加节点的概率符合泊松分布(λ=0.5), 从分布中可以看出来:

bin 中链表长度 = 8 时, 新节点加入的概率非常小, 而长度 8 也是从链表转变成红黑树的边界, 所以, 当加载因子为 0.75 的时候, 几乎不存在转换成红黑树的情况. 大于 0.75 则冲突概率增加, 转换成红黑树的几率增加. 小于 0.75 则浪费更多空间.

0:    0.60653066   // 加载因子=0.75时, bin中链表长度=0, 随机节点落入 bin 中的概率
1:    0.30326533   // 加载因子=0.75时, bin中链表长度=1, 随机节点落入 bin 中的概率
2:    0.07581633   // ...
3:    0.01263606
4:    0.00157952
5:    0.00015795
6:    0.00001316
7:    0.00000094
8:    0.00000006   // 加载因子=0.75时, bin中链表长度=8, 随机节点落入 bin 中的概率

负载因子可以在实例化 HashMap 时指定, 其值可以是任意大于 0 的浮点数, 但不能小于零(小于 0 抛出异常 java.lang.IllegalArgumentException).

扩容实现

HashMap 内部的变量 threshold 保存了下一次需要扩容时的 entry 数量门限值, 计算公式: threshold = 当前容量 * 负载因子. 使用 threshold 可以避免重复的计算.

性能选择

迭代性能

Iterate 性能与 table 的稀疏程度有关, table 比较稠密 (即比较少的空 bin), 则迭代性能更好, 优化方法:

  • 初始化表容量宜小不宜大. 因为迭代的对象是 table , table 的 size 小一些, 空 bin 出现的可能性就小一些, table 更稠密, 迭代性能更好.
  • 负载因子宜大不宜小. 负载因子越大, table 中的 bins 的使用率越高, 整个 table 更稠密.

查找性能

查找性能与 bins 的 hash 冲突情况有关, 冲突少, 查找效率高. 优化方法:

  • 初始化表容量宜大不宜小, 容量大, hash 冲突少, 查找效率高.
  • 负载因子宜小不宜大, 负载因子越小, 碰撞的几率越小.

appendix

  • Table: It is a array of `Object of either Node or TreeNode.
  • Bin: A bin refer to a bucket that is a element in array of table in hashmap.

vs HashTable

HashMapHashTable
线程不安全线程安全
允许 null key 和 null value不允许 null key 或者 null value
Java 1.2 加入Java 最早加入
faster更慢, 不建议使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值