当加载因子大于1时,java中的hashMap是如何填充的?

时间:2015-07-28 06:41:42

标签: java hashmap

我尝试使用以下详细信息创建HashMap: -

HashMap<Integer,String> test = new HashMap<Integer,String>();
test.put(1, "Value1");
test.put(2, "Value2");
test.put(3, "Value3");
test.put(4, "Value4");
test.put(5, "Value5");
test.put(6, "Value6");
test.put(7, "Value7");
test.put(8, "Value8");
test.put(9, "Value9");
test.put(10, "Value10");
test.put(11, "Value11");
test.put(12, "Value12");
test.put(13, "Value13");
test.put(14, "Value14");
test.put(15, "Value15");
test.put(16, "Value16");
test.put(17, "Value17");
test.put(18, "Value18");
test.put(19, "Value19");
test.put(20, "Value20");

我看到每个输入都放在一个不同的桶中。这意味着为每个键计算了不同的哈希码。 现在,    如果我按如下方式修改我的代码: -

HashMap<Integer,String> test = new HashMap<Integer,String>(16,2.0f);
test.put(1, "Value1");
test.put(2, "Value2");
test.put(3, "Value3");
test.put(4, "Value4");
test.put(5, "Value5");
test.put(6, "Value6");
test.put(7, "Value7");
test.put(8, "Value8");
test.put(9, "Value9");
test.put(10, "Value10");
test.put(11, "Value11");
test.put(12, "Value12");
test.put(13, "Value13");
test.put(14, "Value14");
test.put(15, "Value15");
test.put(16, "Value16");
test.put(17, "Value17");
test.put(18, "Value18");
test.put(19, "Value19");
test.put(20, "Value20");

我发现放在不同存储桶中的一些值现在放在一个已经包含一些值的存储桶中,即使它们的散列值不同。任何人都可以帮我理解一下吗?

由于

4 个答案:

答案 0 :(得分:9)

因此,如果在未指定初始大小和加载因子的情况下初始化HashMap,则会初始化大小为16且加载因子为0.75。这意味着,一旦HashMap至少(初始大小*加载因子)大,那么12个元素大,它将被重新定义,这意味着,它将增长到大约两倍的大小,并且所有元素将被重新添加。

现在,您将加载因子设置为2,这意味着,现在只有当地图填充了至少32个元素时,地图才会被重新定位。

现在发生的事情是具有相同hash mod bucketcount的元素将被放入同一个存储桶中。每个包含多个元素的存储桶都包含一个列表,其中包含所有元素。现在,当您尝试查找其中一个元素时,它首先使用哈希查找存储桶。然后它必须迭代该存储桶中的整个列表以找到具有完全匹配的哈希。这是非常昂贵的。

所以最后有一个权衡:重组是相当昂贵的,所以你应该尽量避免它。另一方面,如果存储桶中有多个元素,则查找会非常昂贵,因此您应该尽量避免使用它。所以你需要在这两者之间取得平衡。另一种方法是将初始大小设置得相当高,但这会占用更多未使用的内存。

答案 1 :(得分:3)

在第二次测试中,初始容量为16,加载因子为2.这意味着HashMap将使用16个元素的数组来存储条目(即有16个桶),这个数组仅当地图中的条目数达到32(16 * 2)时才会调整大小。

这意味着一些具有不同hashCodes的密钥必须存储在同一个桶中,因为桶的数量(16)小于条目的总数(在您的情况下为20)。

分配给桶的密钥分为3个步骤:

  1. 首先调用hashCode方法。
  2. 然后在hashCode上应用了一个附加功能,以减少可能由于hashCode实施造成的损害。
  3. 最后,对上一步的结果应用模数运算,以获得0capacity - 1之间的数字。
  4. 第三步是具有不同hashCode s的密钥可能最终位于同一个存储桶中。

答案 2 :(得分:2)

让我们用例子检查 -

i)在第一种情况下,load factor为0.75f,initialCapacity为16,这意味着当HashMap中的桶数达到16 * 0.75 = 12时,将发生数组调整大小。

现在,每个密钥都有不同的HashCode,因此HashCode modulo 16是唯一的,这导致所有前12个条目进入不同的存储区,之后发生调整大小,并且当新条目被放置时它们也会以不同的方式结束桶(HashCode modulo 32是唯一的。)

ii)在第二种情况下,load factor是2.0f,这意味着当没有时会发生调整大小。铲斗达到16 * 2 = 32。 你继续把条目放在地图中,它从不调整大小(对于20个条目),使多个条目发生冲突。

因此,简而言之,在第一个示例中 - 前{12}个条目HashCode modulo 16和所有条目HashCode modulo 32是唯一的,而在第二个案例中,对于所有不是唯一的条目,它总是HashCode modulo 16 (不能因为所有20个条目都必须容纳在16个桶中)

答案 3 :(得分:0)

javadoc解释:

  

HashMap的一个实例有两个影响其性能的参数:   初始容量和负载系数。容量是数量   哈希表中的桶,而初始容量就是   创建哈希表时的容量。负载系数是a   衡量哈希表在其之前可以获得多长的度量   容量自动增加。当中的条目数   哈希表超出了加载因子和当前的乘积   容量,哈希表重新哈希(即内部数据   重建结构),以便哈希表大约两次   桶的数量。

     

作为一般规则,默认加载因子(.75)提供了一个好处   时间和空间成本之间的权衡。值越高,值越低   空间开销,但增加了查找成本(反映在大多数   HashMap类的操作,包括get和put)。预期的   应该考虑地图中的条目数量及其加载因子   帐户设置其初始容量时,以便最小化   重新运算的次数。如果初始容量大于   条目的最大数量除以加载因子,没有重新哈希   操作将永远发生。

默认情况下,初始容量为16,负载系数为0.75。 因此,当条目数超过12 (16 * 0.75)时,其容量将增加到32并且哈希表被重新哈希。这就是为什么在你的第一种情况下,每个不同的元素都有自己的桶。

在第二种情况下,只有当条目数超过32(16*2)时,才会调整哈希表的大小。即使元素具有不同的哈希码值,当计算哈希码%bucketsize时,它也可能发生冲突。这就是你在同一个桶中看到多个元素的原因