【问题标题】:why hashmap resize based on total size instead of filled buckets为什么 hashmap 根据总大小而不是填充的桶来调整大小
【发布时间】:2018-04-20 23:24:50
【问题描述】:

我有一个疑问::

目前java中的HashMap,当totalSize(no of elements inserted) > arrayLength * loadFactor时调整大小

所以它使表格翻倍并重新散列所有键值。

但是假设 Key 类中的 hashcode 被硬编码为 1,所以每次元素都会以链表的方式插入到索引 1 处。但是我们的bucketarray 将不必要地调整总大小。所以它会继续增加bucketarray 的大小,而元素在同一个桶中使用这种哈希码实现。

我有一个问题,我们是否应该检查已填充存储桶的调整大小,而不是总大小??

我知道这样的哈希码会影响性能。我问这是一个合乎逻辑的问题。

【问题讨论】:

  • 仅供参考,它是not always a linked list
  • 使用总元素简化了实现,而不是跟踪使用的存储桶。很可能 HashMap 开发人员没有将他们的设计基于使用他们不需要的 HashMap 的人,或者做了一些非常愚蠢的事情,比如为每个元素使用相同的键,或者提供了一个糟糕的 hashCode() 函数
  • @nos “或以其他方式提供了错误的 hashCode() 函数”实际上, 采取了一些措施来减少不良哈希函数对性能的影响。

标签: java hashmap


【解决方案1】:

想象一个大小为 12 的哈希映射,其中包含 9 个项目。假设巧合的是,#hashCode() 只返回 3 的倍数——它仍然是一个有缺陷的哈希码,但它不是一个人为的边缘情况,例如恒定的哈希码 1。这意味着在这种情况下,只有四个桶(0 , 3, 6, 和 9) 将填充 1 或 2 个元素。

使用您的方法,此哈希图将永远不会调整大小,冲突列表将永远增长,并且性能会受到影响。然而,如果你根据总大小调整它的大小,添加第 10 个元素时的负载因子为 75%,你最终会得到一个包含 24 个桶的地图,其中 8 个将被填充。

基于总大小的增长使冲突列表保持在合理的大小,并具有实际不完美的哈希函数,因为期望每个哈希函数至少做出最佳尝试来分发哈希码是合理的。这意味着增长哈希图将导致比以前更满的桶,即使可能仍然存在集群和空桶。

基本上,您的建议是优化边缘情况下的内存使用,而不是优化访问性能 - 即地图的主要目的 - 在更可能的情况下。

【讨论】:

    【解决方案2】:

    如果hashcode 总是返回相同的值

    1. 这是一个糟糕的实现,没有支持不应该做的事情的逻辑。

    2. hashcode 可能不是常量函数,HashMap 无法知道哈希函数是否为常量类型,因此明智的做法是调整hashmap 的大小,以防hashcode 突然变为非常量函数,然后调整大小可能会导致更好的值分布。

    【讨论】:

      【解决方案3】:

      HashMap 有一些代码试图改进糟糕的hashCode() 实现,但它无法改进总是返回相同值的糟糕hashCode() 实现。

      无论您是否调整HashMap 的大小,这样的hashCode() 都会导致性能不佳。因此,HashMap 的这种错误用法并不能证明您建议添加特殊逻辑是合理的。

      hashCode() 密钥实现的假设是它将在HashMap 箱中尽可能接近均匀地分配密钥。因此,桶中的平均条目数(即条目总数除以桶数)应该可以很好地估计何时应该调整 HashMap 的大小,并且单个桶的大小不需要被检查。

      【讨论】:

      • 我知道这样的哈希码会影响性能。我问这是一个合乎逻辑的问题。
      • @RajatGoyal HashMap 应该以某种方式使用。建议一个不好的用法示例作为更改现有实现的理由(当您正确使用 HashMap 时效果很好)对我来说似乎不合逻辑。
      • @Eran 同意你的看法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多