java 在HashMap中加载因子的意义是什么?

wgeznvg7  于 2023-05-05  发布在  Java
关注(0)|答案(9)|浏览(144)

HashMap有两个重要的属性:sizeload factor。我查阅了Java文档,它说0.75f是初始加载因子。但我找不到它的实际用途。
谁能描述一下我们需要设置负载因子的不同场景,以及不同情况下的一些示例理想值是什么?

qf9go6mv

qf9go6mv1#

documentation解释得很好:
HashMap的示例有两个影响其性能的参数:初始容量和负载系数。容量是哈希表中桶的数量,初始容量只是创建哈希表时的容量。加载因子是哈希表在其容量自动增加之前允许达到的满度的度量。当散列表中的条目的数量超过负载因子和当前容量的乘积时,散列表被重新散列(即,重建内部数据结构),使得散列表具有大约两倍的桶的数量。
作为一般规则,默认加载因子(.75)在时间和空间成本之间提供了良好的折衷。更高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置Map的初始容量时,应考虑Map中的预期条目数及其负载因子,以便最小化rehash操作的数量。如果初始容量大于最大条目数除以加载因子,则不会发生重新散列操作。
与所有性能优化一样,最好避免过早地优化(即没有关于瓶颈在哪里的硬数据)。

d8tt03nd

d8tt03nd2#

HashMap的默认初始容量为16,加载因子为0.75f(即当前Map大小的75%)。负载系数表示HashMap容量应增加一倍的水平。

例如容量和负载系数的乘积为16 * 0.75 = 12。这表示在将第12个键值对存储到HashMap中之后,其容量变为32。

qxgroojn

qxgroojn3#

实际上,根据我的计算,“完美”负载因子更接近log 2(~ 0.7)。尽管小于此值的任何负载因子都将产生更好的性能。我觉得那把点75很可能是从帽子里拿出来的。
证明:
可以避免链接,并且通过预测桶是否为空来利用分支预测。如果桶为空的概率超过0.5,则桶可能为空。
让s表示大小,n表示添加的键的数量。使用二项式定理,桶为空的概率为:

P(0) = C(n, 0) * (1/s)^0 * (1 - 1/s)^(n - 0)

因此,如果存在小于

log(2)/log(s/(s - 1)) keys

当s达到无穷大时,如果添加的密钥的数量使得P(0)= .5,则n/s迅速接近log(2):

lim (log(2)/log(s/(s - 1)))/s as s -> infinity = log(2) ~ 0.693...
yizd12fk

yizd12fk4#

什么是负载因子?

HashMap为增加其容量而要耗尽的容量量。

为什么选择加载因子?

默认情况下,加载因子是初始容量的0.75(16),因此在容量增加之前,25%的桶将是空闲的&这使得许多新的桶在桶数增加之后存在新的哈希码指向它们。

为什么要保留很多空闲存储桶?保留空闲存储桶对性能有什么影响?

如果将加载因子设置为1.0,那么可能会发生一些非常有趣的事情。
假设你正在向hashmap添加一个对象x,它的hashCode是888 &在hashmap中,表示hashcode的bucket是空闲的,所以对象x被添加到bucket中,但是现在再一次说,如果你添加另一个对象y,它的hashCode也是888,那么你的对象y肯定会被添加,但是在桶的末尾(* 因为bucket只不过是存储key,value和next的linkedList实现 *)现在这对性能有影响了!由于你的对象y不再出现在bucket的头部,如果你执行查找,所花费的时间不会是O(1),这一次它取决于同一个bucket中有多少项。顺便说一下,这被称为散列冲突&甚至在加载因子小于1时也会发生这种情况。

性能、哈希冲突和负载因子的相关性

*更低的负载系数=更多的空闲存储桶=更少的碰撞机会=高性能=高空间要求。
*更高的负载系数=更少的空闲存储桶=更高的碰撞机会=更低的性能=更低的空间需求。

txu3uszq

txu3uszq5#

关于documentation
加载因子是一个度量,用来衡量哈希表在容量自动增加之前允许达到的满度
这实际上取决于您的特定要求,没有“经验法则”来指定初始负载系数。

q9rjltbz

q9rjltbz6#

对于HashMapDEFAULT_INITIAL_CAPACITY = 16DEFAULT_LOAD_FACTOR = 0.75f,这意味着HashMap中所有条目的最大数量=16 * 0.75 = 12。当第十三个元素被添加时,HashMap的容量(数组大小)将翻倍!完美的插图回答了这个问题:x1c 0d1x图像取自此处:
https://javabypatel.blogspot.com/2015/10/what-is-load-factor-and-rehashing-in-hashmap.html

wh6knrhe

wh6knrhe7#

如果桶太满了,我们就得翻
一个很长的链表
这有点违背了重点。
这里有一个例子,我有四个桶。
到目前为止,我的哈希集中有大象和獾。
这是一个很好的情况,不是吗?
每个元素都有零个或一个元素。
现在我们将两个元素放入HashSet中。

buckets      elements
      -------      -------
        0          elephant
        1          otter
         2          badger
         3           cat

这也不算太糟。
每个桶只有一个元素。如果我想知道,里面有Pandas吗?
我可以很快地看一下1号桶
那里和
我就知道这不是我们的收藏。
如果我想知道里面有没有猫,我就看水桶
3号
我找到猫,我很快就知道它是否在我们的
收藏。
如果我加上树袋熊呢,那也不算太糟。

buckets      elements
      -------      -------
        0          elephant
        1          otter -> koala 
         2          badger
         3           cat

也许现在不是在1号桶里只看
一个元件,
我要看两个。
但至少我不用看大象獾和

如果我再找Pandas的话,它只能在桶里
1号和
我不需要看水獭和其他东西
考拉。
但现在我把鳄鱼放在1号桶里你就可以
看看会发生什么
如果一号桶一直变大
我基本上就得看完
这些元素
应该在1号桶里的东西

buckets      elements
      -------      -------
        0          elephant
        1          otter -> koala ->alligator
         2          badger
         3           cat

如果我开始向其他桶添加字符串,
对,问题越来越大
单桶
我们怎样才能避免水桶太满呢?
这里的解决方案是

"the HashSet can automatically

        resize the number of buckets."

HashSet实现了桶的
太满了。
它正在失去这种优势,这一切的一个查找
元素。
它只会创建更多的桶(通常是以前的两倍)和
然后将元件放入正确的桶中。
下面是我们的基本HashSet实现,其中
现在我将创建一个“自调整大小的HashSet”。
这个HashSet会意识到桶是
吃得太饱了
它需要更多的桶。
loadFactor是HashSet类中的另一个字段。
loadFactor表示每个
桶,
我们要在上面调整大小。
loadFactor是空间和时间之间的平衡。
如果桶太满了,我们就调整大小。
当然这需要时间但是
如果桶是一个
再空一点。
让我们看一个例子。
这是一个HashSet,到目前为止我们已经添加了四个元素。
大象,狗,猫和鱼。

buckets      elements
      -------      -------
        0          
        1          elephant
         2          cat ->dog
         3           fish
          4         
           5

在这一点上,我已经决定loadFactor
阈值,
每个桶的平均元素数
是0.75。
存储桶的数量是bucket.length,它是6,而
此时,我们的HashSet有四个元素,因此
当前大小为4。
我们将调整HashSet的大小,也就是说,我们将添加更多的桶,
当每个桶的平均元素数超过
的loadFactor。
即当前大小除以bucket.length为
大于loadFactor。
此时,每个桶的平均元素数
是4除以6。
4个元素,6个桶,即0.67。
这比我设定的阈值0.75小
好的。
我们不需要调整大小。
但现在我们再加上土拨鼠。

buckets      elements
      -------      -------
        0          
        1          elephant
         2        woodchuck-> cat ->dog
         3           fish
          4         
           5

土拨鼠最后会在3号桶里。
此时,currentSize为5。
现在每个桶的平均元素数
是currentSize除以buckets.length。
5个元素除以6个桶的结果是0.83。
这超过了0.75的loadFactor。
为了解决这一问题,为了使
桶也许有点
更空,以便像确定
桶包含
一个元素会稍微简单一点,我想调整大小
我的哈希集
调整HashSet的大小需要两个步骤。
首先我将桶的数量加倍,我有6个桶,
现在我有12桶了。
注意这里我设置为0.75的loadFactor保持不变。
但是改变的桶的数量是12,
元素的数量保持不变,为5。
5除以12大约是0.42,这远低于我们的
loadFactor,
所以我们现在没事了
但我们还没有完成因为其中一些元素在
现在是错误的桶。
例如,大象。
大象在2号桶里是因为
大象中的人物
是8。
我们有6个桶,8减6等于2。
这就是为什么它最终排在第二位。
但现在我们有12个桶,8mod12等于8,所以
大象不再属于2号桶了
大象属于8号桶。
土拨鼠呢?
土拨鼠是这整个问题的始作俑者。
土拨鼠最后在3号桶里。
因为9模6等于3。
但现在我们做9模12。
9模12等于9土拨鼠去9号桶
你可以看到这一切的好处。
现在桶号3只有两个元素,而之前有3个。
这是我们的代码
我们的散列集有单独的链接
没有做任何调整。
现在,这里有一个新的实现,我们使用调整大小。
大部分代码都是一样的
我们还是要确定它是否包含
值已经。
如果没有,我们就能知道是哪个桶
应该进入和
然后把它加到那个桶里,加到那个LinkedList里。

但是现在我们递增currentSize字段。
currentSize是跟踪数字的字段
我们的HashSet中的元素。
我们要递增它然后我们要看
在平均负载下,
每个桶的平均元素数。
我们在这里做除法。
我们得在这里做一点选角来确保
我们得到一个替身
然后,我们将把平均负载与磁场进行比较
我设定为
0.75当我创建这个HashSet的时候,例如,
的loadFactor。
如果平均负载大于loadFactor,
这意味着每个桶有太多的元素
平均水平,我需要重新插入。
下面是要重新插入的方法的实现
所有的元素。
首先,我将创建一个名为oldBuckets的局部变量。
也就是说现在的水桶
然后我就开始调整所有东西的大小
注意我现在还没有创建一个新的链表数组。
我只是把桶重命名为oldBuckets。
现在记住水桶是我们课上的一个领域,我要去
现在创建一个新阵列
但是这将有两倍多的元素
就像第一次一样
现在我需要重新插入
我将遍历所有旧的桶。
oldBuckets中的每个元素都是字符串的LinkedList
那是个桶。
我会遍历这个桶,得到里面的每个元素
水桶
现在我要把它重新插入newBuckets。
我会得到它的hashCode。
我会找出是哪个指数。
现在我得到了新的桶,新的LinkedList
字符串和
我会把它加到那个新桶里
简单回顾一下,我们所看到的HashSet是一个链表数组。
列表或桶。
自调整大小的HashSet可以使用一些比率或

kx7yvsdv

kx7yvsdv8#

我会选择n * 1.5或n +(n〉〉1)的表大小,这将给予.66666~的负载因子,而没有除法,这在大多数系统上都很慢,特别是在硬件中没有除法的便携式系统上。

qoefvg9y

qoefvg9y9#

hashmap中的Load Factor负责增加hashmap的容量,默认的load factor是hashmap容量的75%,load factor的默认值是0.75f。我们可以在hashset中使用它作为HashSet(int i[初始容量],float a[加载因子]);decalaration和intialization:-

HashSet<>myHashset=new HashSet<>(8,0.8f);

这里8是哈希集的初始容量,0.8是哈希集的负载因子。

相关问题