【问题标题】:Query about internal implementation of HashMap查询HashMap内部实现
【发布时间】:2019-05-20 19:24:13
【问题描述】:

我正在通过 HashMap 实现并参考此链接:How does Java implement hash tables? 我发现“一个 HashMap 包含一个桶数组以包含其条目”。所以,我有几个问题-

  1. 存储桶数组的类型是什么。
  2. 既然数组有缺点(例如,固定大小并且只允许同质数据)。那么为什么尽管有这些缺点,我们仍然使用数组。

3.如果键或冲突的哈希码相同,它使用链表。它如何获取(搜索)第二个,第三个节点等的引用。

感谢您的建议。

【问题讨论】:

  • 要查看它是如何实现的,您只需下载该版本的 JDK。源代码在 src.zip 文件中提供。如果您在理解来源时遇到问题,请随时提出具体问题。

标签: java hashmap hashtable


【解决方案1】:
  1. 存储桶数组的类型是什么。

这取决于您制作的地图,如果您制作HashMap<Integer, String>,那么桶将属于这些类型,能够包含这些类型的对象

  1. 既然数组有缺点(例如,固定大小并且只允许同质数据)。那么为什么尽管有这些缺点,我们仍然使用数组。

因为与性能提升相比,缺点是值得的。因为数组是固定大小的,所以可以跳过很多检查(即这个索引是否存在?)。 你可以在这里阅读更多相关信息; https://en.wikiversity.org/wiki/Java_Collections_OverviewWhy not always use ArrayLists in Java, instead of plain ol' arrays?

  1. 如果键或冲突的哈希码相同,它使用链表。它如何获取(搜索)第二个、第三个节点等的引用。

这比我能解释得更好; What happens when a duplicate key is put into a HashMap?

【讨论】:

    【解决方案2】:
    1. 它是一个内部对象,包含键、值和对桶中下一个节点的引用(实现单链表)
    2. 阵列需要 2 的幂的固定大小。给定键的数组索引基于键的哈希码的逻辑与 (&) 和数组的大小,这是哈希表的实际“魔力”。
    3. 需要桶中的链表来处理哈希码冲突。这就是 HashMap.get() 的 O(n) 的最坏情况复杂性的原因 - 如果所有键具有相同的哈希码并且搜索的键是存储桶中的最后一个键,则会发生这种情况。

    如果 hashmaps 增长,则需要一个非常昂贵的 rehash 函数,因为数组也必须增长到 2 的下一个幂。在这种情况下,每个桶都必须重新计算其索引。在这种情况下,将构造一个新数组。这意味着不需要动态数据结构。

    如果您使用合适的容量参数创建新的哈希图,则可以避免重新哈希。

    【讨论】:

      【解决方案3】:

      来自OpenJDK8 code source

      1. 垃圾箱是列表或树,具体取决于它们包含的元素数量
      2. 在这种情况下,数组的同质性不是问题,访问速度优先于调整数组大小的成本
      3. HashMap 总是遍历所有具有相同哈希的值,测试它们是否具有正确的键:
      final Node<K,V> getNode(int hash, Object key) {
          Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
          if ((tab = table) != null && (n = tab.length) > 0 &&
              (first = tab[(n - 1) & hash]) != null) {
              if (first.hash == hash && // always check first node
                  ((k = first.key) == key || (key != null && key.equals(k))))
                  return first;
              if ((e = first.next) != null) {
                  if (first instanceof TreeNode)
                      return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                  do {
                      if (e.hash == hash &&
                          ((k = e.key) == key || (key != null && key.equals(k))))
                          return e;
                  } while ((e = e.next) != null);
              }
          }
          return null;
      
      }
      

      【讨论】:

      • 感谢您的回答。对于第 1 点 - 因为树不允许重复。所以假设我输入了重复的值。它将如何管理?
      • @ManojKnitan 树节点的值不是地图的值,它们扩展了Entry&lt;K,V&gt;。在链接代码中搜索TreeNode 类。
      猜你喜欢
      • 1970-01-01
      • 2013-11-13
      • 2011-03-02
      • 2016-12-12
      • 2017-05-18
      • 1970-01-01
      • 2015-09-28
      • 2015-08-01
      • 2013-09-30
      相关资源
      最近更新 更多