【问题标题】:In HashMap why threshold value (The next size value at which to resize) is capacity * load factor. Why not as equal to size or capacity of map在 HashMap 中,为什么阈值(要调整大小的下一个大小值)是容量 * 负载因子。为什么不等于地图的大小或容量
【发布时间】:2015-02-07 15:48:59
【问题描述】:

在 HashMap 中,为什么阈值(要调整大小的下一个大小值)是 容量 * 负载 因子。为什么不等于大小地图容量

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

当我们添加第 13 个元素时地图调整大小为什么会这样,为什么地图的作者决定保留它capacity * load factor(即 12)?为什么不与容量相同(即 16)。

为什么不保持阈值等于容量,以便仅在 hashmap 已满时才进行重新散列?

【问题讨论】:

    标签: java collections


    【解决方案1】:

    Javadoc、Javadoc、Javadoc。那是你看的第一个地方。在HashMap 上面写着:

    作为一般规则,默认负载因子 (.75) 提供了一个很好的折衷方案 时间成本和空间成本之间。较高的值会减少空间开销 但增加了查找成本(反映在 HashMap 类,包括 get 和 put)。这 应采用地图中的预期条目数及其负载因子 在设置其初始容量时考虑,以尽量减少 重新哈希操作的数量。如果初始容量更大 大于最大条目数除以负载因子,否 重新哈希操作将永远发生。

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

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


    可以在 Knuth“计算机编程的艺术”第 3 卷中找到权衡的一般概述。

    【讨论】:

    • @Ordus 感谢您的回答,但我非常清楚负载因子的重要性,但我的问题是当我们保持阈值 = 大小时有什么问题?如果您对我的问题有任何疑问,请告诉我
    • @niiraj874u 我在您输入评论时添加了一些内容。如果哈希映射已满,它的性能可能会很快下降,具体取决于存储桶的组织方式。
    • @Ordus 所以我说的是我的理解,而不是告诉我我是否正确理解了你。当您保持阈值 = 大小时,它不会显示性能下降(当然它可能有,但我们可以忽略的非常少),但当大小为数千或 lac 时,它会对性能产生影响。 .
    • @niiraj874u 有点,是的。 Knuth 在“计算机编程的艺术”第 3 卷中对其进行了深入探讨。更深入的探索需要相当多的数学和概率知识。当然,这仅适用于 Java 链表作为存储桶的实现。如果 hashmap 已满,分散或开放寻址实现将停止工作。
    • @niiraj874u:如果您“非常了解负载因子的重要性”,那么您的问题就没有多大意义。毕竟,将threshold 确定为threshold = capacity * load factorload factor唯一 目的。拥有一个threshold==capacity 相当于拥有一个load factor
    【解决方案2】:

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

    【讨论】:

    • 感谢您的回答,我们如何确保在低阈值的情况下发生低碰撞?当我在 HashMap 中添加 500 个元素时,阈值大小变为 768,大小变为 1024。所以,即使我有 500 个元素,大小也是 1024,当我添加第 769 个元素时,它将是 2048。所以在这里我们可以看到空间的浪费。
    【解决方案3】:

    HashMap 实现允许您设置负载因子。这种设计决策使类的用户可以对调整基础数据结构大小的条件进行一定程度的控制。

    0.75 的默认负载因子值很可能被选为内存使用和地图性能(由冲突率和调整大小开销确定)之间的合理平衡。

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

    顺便说一句,您对“完整”HashMap 的概念有点歪曲。该实现可以很好地处理任意数量的冲突(尽管存在冲突的性能成本)。您可以使用负载因子为 10 亿的 HashMap,并且它(可能)永远不会超过 16 的容量。

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

    【讨论】:

    • 我的问题是当我们保持阈值=大小时有什么问题?
    • @niiraj874u 我编辑了我的答案以专门解决这个问题。
    • 我从你的最后一段中明白了。谢谢.. +1
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2019-03-11
      • 1970-01-01
      • 2011-10-30
      • 2018-11-03
      • 2018-04-20
      • 2014-10-08
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多