千家信息网

为什么HashMap的加载因子是0.75

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,为什么HashMap的加载因子是0.75,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。为什么HashMap需要加载因子?HashMap的
千家信息网最后更新 2026年11月03日为什么HashMap的加载因子是0.75

为什么HashMap的加载因子是0.75,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

为什么HashMap需要加载因子?

HashMap的底层是哈希表,是存储键值对的结构类型,它需要通过一定的计算才可以确定数据在哈希表中的存储位置:

static final int hash(Object key) {    int h;    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}// AbstractMappublic int hashCode() {     int h = 0;     Iterator> i = entrySet().iterator();     while (i.hasNext())         h += i.next().hashCode();     return h;}

一般的数据结构,不是查询快就是插入快,HashMap就是一个插入慢、查询快的数据结构。

但这种数据结构容易产生两种问题:① 如果空间利用率高,那么经过的哈希算法计算存储位置的时候,会发现很多存储位置已经有数据了(哈希冲突);② 如果为了避免发生哈希冲突,增大数组容量,就会导致空间利用率不高。

而加载因子就是表示Hash表中元素的填满程度。

加载因子 = 填入表中的元素个数 / 散列表的长度

加载因子越大,填满的元素越多,空间利用率越高,但发生冲突的机会变大了;

加载因子越小,填满的元素越少,冲突发生的机会减小,但空间浪费了更多了,而且还会提高扩容rehash操作的次数。

冲突的机会越大,说明需要查找的数据还需要通过另一个途径查找,这样查找的成本就越高。因此,必须在"冲突的机会"与"空间利用率"之间,寻找一种平衡与折衷。

解决冲突有什么方法?

1. 开放定址法

Hi = (H(key) + di) MOD m,其中i=1,2,…,k(k<=m-1)

H(key)为哈希函数,m为哈希表表长,di为增量序列,i为已发生冲突的次数。其中,开放定址法根据步长不同可以分为3种:

1.1 线性探查法(Linear Probing):di = 1,2,3,…,m-1

简单地说,就是以当前冲突位置为起点,步长为1循环查找,直到找到一个空的位置,如果循环完了都占不到位置,就说明容器已经满了。举个栗子,就像你在饭点去街上吃饭,挨家去看是否有位置一样。

1.2 平方探测法(Quadratic Probing):di = ±12, ±22,±32,…,±k2(k≤m/2)

相对于线性探查法,这就相当于的步长为di = i2来循环查找,直到找到空的位置。以上面那个例子来看,现在你不是挨家去看有没有位置了,而是拿手机算去第i2家店,然后去问这家店有没有位置。

1.3 伪随机探测法:di = 伪随机数序列

这个就是取随机数来作为步长。还是用上面的例子,这次就是完全按心情去选一家店问有没有位置了。

但开放定址法有这些缺点:

  • 这种方法建立起来的哈希表,当冲突多的时候数据容易堆集在一起,这时候对查找不友好;

  • 删除结点的时候不能简单将结点的空间置空,否则将截断在它填入散列表之后的同义词结点查找路径。因此如果要删除结点,只能在被删结点上添加删除标记,而不能真正删除结点;

  • 如果哈希表的空间已经满了,还需要建立一个溢出表,来存入多出来的元素。

2. 再哈希法

Hi = RHi(key), 其中i=1,2,…,k

RHi()函数是不同于H()的哈希函数,用于同义词发生地址冲突时,计算出另一个哈希函数地址,直到不发生冲突位置。这种方法不容易产生堆集,但是会增加计算时间。

所以再哈希法的缺点是:增加了计算时间。

3. 建立一个公共溢出区

假设哈希函数的值域为[0, m-1],设向量HashTable[0,…,m-1]为基本表,每个分量存放一个记录,另外还设置了向量OverTable[0,…,v]为溢出表。基本表中存储的是关键字的记录,一旦发生冲突,不管他们哈希函数得到的哈希地址是什么,都填入溢出表。

但这个方法的缺点在于:查找冲突数据的时候,需要遍历溢出表才能得到数据。

4. 链地址法(拉链法)

将冲突位置的元素构造成链表。在添加数据的时候,如果哈希地址与哈希表上的元素冲突,就放在这个位置的链表上。

拉链法的优点:

  • 处理冲突的方式简单,且无堆集现象,非同义词绝不会发生冲突,因此平均查找长度较短;

  • 由于拉链法中各链表上的结点空间是动态申请的,所以它更适合造表前无法确定表长的情况;

  • 删除结点操作易于实现,只要简单地删除链表上的相应的结点即可。

拉链法的缺点:需要额外的存储空间。

从HashMap的底层结构中我们可以看到,HashMap采用是数组+链表/红黑树的组合来作为底层结构,也就是开放地址法+链地址法的方式来实现HashMap。

为什么HashMap加载因子一定是0.75?而不是0.8,0.6?

从上文我们知道,HashMap的底层其实也是哈希表(散列表),而解决冲突的方式是链地址法。HashMap的初始容量大小默认是16,为了减少冲突发生的概率,当HashMap的数组长度到达一个临界值的时候,就会触发扩容,把所有元素rehash之后再放在扩容后的容器中,这是一个相当耗时的操作。

而这个临界值就是由加载因子和当前容器的容量大小来确定的:

临界值 = DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR

即默认情况下是16x0.75=12时,就会触发扩容操作。

那么为什么选择了0.75作为HashMap的加载因子呢?这个跟一个统计学里很重要的原理--泊松分布有关。

泊松分布是统计学和概率学常见的离散概率分布,适用于描述单位时间内随机事件发生的次数的概率分布。有兴趣的读者可以看看维基百科或者阮一峰老师的这篇文章:泊松分布和指数分布:10分钟教程[1]

等号的左边,P 表示概率,N表示某种函数关系,t 表示时间,n 表示数量。等号的右边,λ 表示事件的频率。

在HashMap的源码中有这么一段注释:

* Ideally, under random hashCodes, the frequency of* nodes in bins follows a Poisson distribution* (http://en.wikipedia.org/wiki/Poisson_distribution) with a* parameter of about 0.5 on average for the default resizing* threshold of 0.75, although with a large variance because of* resizing granularity. Ignoring variance, the expected* occurrences of list size k are (exp(-0.5) * pow(0.5, k) /* factorial(k)). The first values are:* 0:    0.60653066* 1:    0.30326533* 2:    0.07581633* 3:    0.01263606* 4:    0.00157952* 5:    0.00015795* 6:    0.00001316* 7:    0.00000094* 8:    0.00000006* more: less than 1 in ten million

在理想情况下,使用随机哈希码,在扩容阈值(加载因子)为0.75的情况下,节点出现在频率在Hash桶(表)中遵循参数平均为0.5的泊松分布。忽略方差,即X = λt,P(λt = k),其中λt = 0.5的情况,按公式:

计算结果如上述的列表所示,当一个bin中的链表长度达到8个元素的时候,概率为0.00000006,几乎是一个不可能事件。

所以我们可以知道,其实常数0.5是作为参数代入泊松分布来计算的,而加载因子0.75是作为一个条件,当HashMap长度为length/size ≥ 0.75时就扩容,在这个条件下,冲突后的拉链长度和概率结果为:

0:    0.606530661:    0.303265332:    0.075816333:    0.012636064:    0.001579525:    0.000157956:    0.000013167:    0.000000948:    0.00000006

那么为什么不可以是0.8或者0.6呢?

HashMap中除了哈希算法之外,有两个参数影响了性能:初始容量和加载因子。初始容量是哈希表在创建时的容量,加载因子是哈希表在其容量自动扩容之前可以达到多满的一种度量。

在维基百科来描述加载因子:

对于开放定址法,加载因子是特别重要因素,应严格限制在0.7-0.8以下。超过0.8,查表时的CPU缓存不命中(cache missing)按照指数曲线上升。因此,一些采用开放定址法的hash库,如Java的系统库限制了加载因子为0.75,超过此值将resize散列表。

在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少扩容rehash操作次数,所以,一般在使用HashMap时建议根据预估值设置初始容量,以便减少扩容操作。

选择0.75作为默认的加载因子,完全是时间和空间成本上寻求的一种折衷选择。

看完上述内容,你们掌握为什么HashMap的加载因子是0.75的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!

哈希 冲突 因子 位置 数据 空间 元素 容量 结点 地址 函数 时候 概率 方法 结构 长度 存储 开放 就是 情况 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 麻工互联网科技 互联网科技美容 互联网和高科技公司 成都互联网科技公司100强 杭州壁岐互联网科技有限公司 互联网科技与大数据专业 哈尔滨屹滨互联网科技有限公司 青海科技产业互联网发展 天津腾达互联网科技有限公司 互联网新科技动漫ppt素材 佛山市拉法互联网科技有限公司 高科技互联网发展前景如何 恒生互联网科技业指数调整周期 杭州披云加互联网科技有限公司 富国互联网科技002692 上海同邻互联网科技有限公司规模 互联网界的黑科技 互联网公司属于科技吗 互联网科技和医药行业 黑龙江互联网科技公司 互联网科技公司起名 港股互联网科技股 汇久创客互联网科技 赶超互联网科技 微信多开 开一家科技互联网公司需要多少钱 智帮互联网科技公司最近动向 信承诺互联网科技有限公司 兰石爱特工业互联网科技分公司 山东蛋芒互联网科技有限公司 知域互联网科技有限公司官网
0