在HashMap中,为什么阈值(resize的下一个大小值)是capacity * load factor。 为什么不等于地图的大小或容量

在HashMap中,为什么阈值(resize的下一个大小值)是capacity * load factor。 为什么不等于 地图的 大小容量

例如,最初默认容量= 16,负载系数= 0.75,因此threshold = (capacity * load factor) = (16 * 0.75) = 12

添加第13个元素的地图resize为什么这样,为什么地图的作者决定保持capacity * load factor (12)? 为什么不与容量相同(16)。

为什么不保持阈值等于容量,以便只在hashmap满了时才进行重组?

Javadoc,Javadoc,Javadoc。 这是你看的第一个地方。 在HashMap它说:

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

就像哈希映射理论一样 – 如果你的地图已经满了,那么你正在做一些非常非常错误的事情。 到那时你可能在O(sqrt(N))上使用随机数据进行查找 – BAD 。 您永远不希望您的hashmap已满。 但是一张非常稀疏的地图会浪费太多空间(正如你所注意到的那样),并且需要很长时间才能完成迭代。 因此,对于大多数用例, 应该有一个小于1的加载因子。

注意:“浪费的空间”与地图的大小成正比,与负载系数成反比。 但是查找时间具有更复杂的预期性能函数。 这意味着相同的加载因子将不适用于不同大小的哈希映射 – 因为它将意味着不同的规模权衡。


关于权衡的一般概述可以在Knuth“The Art of Computer Programming”第3卷中找到。

从理论的角度来看,保持与完整哈希表没有冲突的可能性非常低,因此哈希表将被resize以维持其所需的O(1)查找属性 – 更少的冲突意味着更直接地访问条目和更少的搜索。

HashMap实现允许您设置加载因子。 该设计决策为该类的用户提供了对基础数据结构resize的条件的一些控制措施。

默认加载因子值0.75可能被选择为内存使用和映射性能之间的合理平衡(由冲突率和resize开销确定)。

对于任何给定的HashMap实例,您可以根据具体情况选择合适的加载因子。 您需要考虑小内存占用的相对重要性,您对查找的性能敏感程度,以及您对put的性能敏感程度(放置导致地图重建的速度非常慢)。

另外,你对“完整”HashMap的概念有点偏差。 该实现处理任意数量的冲突就好了(尽管冲突有性能成本)。 您可以使用负载因子为10亿的HashMap,它(可能)永远不会超过16的容量。

将加载因子设置为1.0没有问题,这会在将第17个元素添加到默认大小的HashMap时导致重新哈希操作。 与默认值0.75相比,您将使用更少的空间,更少的重做,并且有更多的冲突(因此在链表中使用equals()进行搜索)。

在java 8中,当达到阈值时,桶的内容从使用对象到平衡树之间的链接切换,这提高了从O(n)到O(log n)的性能。 这是java 8中有时需要记住的function之一