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

问题描述 投票:206回答:8

HashMap有两个重要的属性:sizeload factor。我浏览了Java文档,并说0.75f是初始加载因子。但我找不到它的实际用途。

有人可以描述我们需要设置负载因子的不同场景以及针对不同情况的一些样本理想值吗?

java hashmap load-factor
8个回答
239
投票

documentation很好地解释了它:

HashMap的一个实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。加载因子是在自动增加容量之前允许哈希表获取的完整程度的度量。当哈希表中的条目数超过加载因子和当前容量的乘积时,哈希表将被重新哈希(即,重建内部数据结构),以便哈希表具有大约两倍的桶数。

作为一般规则,默认加载因子(.75)在时间和空间成本之间提供了良好的权衡。较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put)。在设置其初始容量时,应考虑映射中的预期条目数及其加载因子,以便最小化重新散列操作的数量。如果初始容量大于最大条目数除以加载因子,则不会发生重新加载操作。

与所有性能优化一样,最好避免过早地优化事物(即没有关于瓶颈所在的硬数据)。


134
投票

HashMap的默认初始容量为16,载荷系数为0.75f(即当前地图大小的75%)。负载系数表示HashMap容量应该加倍的水平。

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


32
投票

实际上,根据我的计算,“完美”载荷因子更接近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...

24
投票

什么是负载系数?

HashMap增加容量所需的容量是多少?

为什么加载因子?

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

现在为什么要保留许多免费存储桶以及保持免费存储桶对性能有何影响?

如果将加载因子设置为1.0,那么可能会发生一些非常有趣的事情。

假设您正在向hashmap中添加一个对象x,其hashCode为888,并且在您的hashmap中,表示哈希码的桶是空闲的,因此对象x被添加到桶中,但现在再次说明是否要添加另一个对象y,其hashCode是还有888那么你的对象y肯定会被添加到桶的末尾(因为桶只是linkList实现存储键,值和下一个)现在这会对性能产生影响!由于你的对象y不再存在于桶的头部,如果你执行查找,所用的时间不会是O(1),这次它取决于同一个桶中有多少项。这称为哈希冲突,当加载因子小于1时甚至会发生这种情况。

性能,哈希冲突和加载因子之间的相关性?

较低的载荷系数=更多的自由铲斗=更少的碰撞机会=高性能=高空间要求。

如果我错在某处,请纠正我。


17
投票

来自documentation

加载因子是在自动增加容量之前允许哈希表获取的完整程度的度量

这实际上取决于您的特定要求,没有“经验法则”来指定初始载荷因子。


2
投票

如果水桶太满,那么我们必须仔细检查

一个很长的链表。

这就是打败这一点。

所以这是一个我有四个桶的例子。

到目前为止,我的HashSet中有大象和獾。

这是一个非常好的情况,对吗?

每个元素都有零个或一个元素。

现在我们在HashSet中添加了两个元素。

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

这也不错。

每个桶只有一个元素。所以,如果我想知道,这是否包含熊猫?

我可以很快看到第1号桶,但事实并非如此

那里和

我知道它不在我们的收藏中。

如果我想知道它是否包含猫,我会看看桶

3号,

我找到猫,我很快就知道它是否在我们身上

采集。

如果我添加考拉怎么办,那不是那么糟糕。

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

也许现在而不是仅仅看着1号桶

一个元素,

我需要看两个。

但至少我不必看大象,獾和傻瓜

猫。

如果我再次寻找熊猫,它只能在桶中

1号和1号

我不需要看其他任何东西然后水獭和

考拉。

但是现在我把鳄鱼放在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将意识到桶是

太满了

它需要更多的桶。

load Factor是我们的HashSet类中的另一个字段。

load Factor表示每个元素的平均数量

桶,

我们想要调整大小。

负载因子是空间和时间之间的平衡。

如果水桶太满,我们会调整大小。

当然,这需要时间,但是

如果水桶是一个,它可以节省我们的时间

多一点空虚。

我们来看一个例子吧。

这是一个HashSet,到目前为止我们已经添加了四个元素。

大象,狗,猫和鱼。

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

此时,我已经决定了loadFactor了

阈,

我认为每桶的平均元素数量

用,是0.75。

buckets.length中的桶数,即6,和

此时我们的HashSet有四个元素,所以

目前的规模是4。

我们将调整HashSet的大小,即我们将添加更多的桶,

当每桶的平均元素数超过时

loadFactor。

那是当前大小除以buckets.length的时候

大于loadFactor。

此时,每桶的平均元素数

是4除以6。

4个元素,6个桶,即0.67。

这比我设定的0.75的阈值低,所以我们就是这样

好的。

我们不需要调整大小。

但现在让我们说添加土拨鼠。

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

Woodchuck最终将排在第3位。

此时,currentSize为5。

现在每桶的平均元素数量

是currentSize除以buckets.length。

这5个元素除以6个桶是0.83。

这超过了0.75的loadFactor。

为了解决这个问题,为了做到这一点

水桶也许有点

更空的所以操作就像确定一个

桶包含

一个元素会有点复杂,我想调整大小

我的HashSet。

调整HashSet的大小需要两个步骤。

首先,我将把桶的数量增加一倍,我有6个桶,

现在我要有12个水桶。

请注意,我设置为0.75的loadFactor保持不变。

但改变的桶数是12,

元素数量保持不变,为5。

5除以12约为0.42,这远低于我们的

loadFactor,

所以我们现在好了。

但是我们没有完成,因为其中一些元素存在

错误的桶现在。

例如,大象。

因为数量大象在2号桶中

大象中的人物

是8。

我们有6个桶,8个减6个是2个。

这就是为什么它最终排在第2位。

但是现在我们有12个桶,8个mod 12是8个,所以

大象不再属于2号桶。

大象属于8号桶。

土拨鼠怎么样?

土拨鼠是开始这整个问题的人。

Woodchuck最终获得了第3名。

因为9 mod 6是3。

但现在我们做9 mod 12。

9 mod 12是9,土拨鼠去9号水桶。

而且你看到了这一切的优势。

现在,3号桶只有两个元素,而之前有3个元素。

所以这是我们的代码,

我们的HashSet有单独的链接

没有做任何调整大小。

现在,这是一个我们使用resizing的新实现。

大部分代码都是一样的,

我们仍然要确定它是否包含

价值已经。

如果没有,那么我们将弄清楚它是哪个桶

应该进入和

然后将其添加到该存储桶,将其添加到该LinkedList。

但现在我们增加currentSize字段。

currentSize是跟踪数字的字段

我们的HashSet中的元素。

我们要增加它,然后我们会去看

在平均负荷下,

每桶的平均元素数。

我们会在这里做那个部门。

我们必须在这里做一些铸造以确保

我们得到了一倍。

然后,我们将平均负载与现场进行比较

我已经设定为

例如,当我创建这个HashSet时,这是0.75

loadFactor。

如果平均负载大于loadFactor,

这意味着每个桶上的元素太多了

平均,我需要重新插入。

所以这是我们重新插入方法的实现

所有元素。

首先,我将创建一个名为oldBuckets的局部变量。

这是指他们目前所处的水桶

在我开始调整一切之前。

注意我还没有创建一个新的链表列表。

我只是将桶重命名为oldBuckets。

现在记得水桶是我们班上的一个领域,我要去

现在创建一个新数组

链接列表,但这将有两倍的元素

因为它是第一次。

现在我需要实际重新插入,

我要遍历所有旧桶。

oldBuckets中的每个元素都是字符串的LinkedList

这是一个桶。

我将通过那个桶并获得每个元素

桶。

现在我要把它重新插入到newBuckets中。

我会得到它的hashCode。

我会弄清楚它是哪个索引。

现在我得到了新的存储桶,新的LinkedList

字符串和

我将它添加到新的存储桶中。

所以回顾一下,正如我们所见,HashSets是Linked的数组

列表或桶。

自调整大小的HashSet可以实现使用一些比例或


1
投票

我会选择一个n * 1.5或n +(n >> 1)的表格大小,这样可以在没有分割的情况下提供.66666的负载系数,这在大多数系统上都很慢,特别是在没有分区的便携式系统上硬件。


0
投票

对于HashMap DEFAULT_INITIAL_CAPACITY = 16和DEFAULT_LOAD_FACTOR = 0.75f,这意味着HashMap中的所有条目的MAX数量= 16 * 0.75 = 12.当第13个元素添加时,HashMap的容量(数组大小)将加倍!完美插图回答了这个问题:enter image description here图像取自这里:

https://javabypatel.blogspot.com/2015/10/what-is-load-factor-and-rehashing-in-hashmap.html


0
投票

完全理解负载因子和重新加速是here

© www.soinside.com 2019 - 2024. All rights reserved.