查询HashMap的内部实现

时间:2018-12-20 09:34:22

标签: java hashmap hashtable

我正在研究HashMap的实现,并引用以下链接:How does Java implement hash tables? 我发现“ HashMap包含一组存储桶以便包含其条目”。所以,我有几个问题-

  1. 桶阵列的类型是什么。
  2. 由于数组具有缺点(例如,固定大小且仅允许同质数据)。因此,尽管有这些缺点,我们为什么仍使用数组。

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

感谢adv。

3 个答案:

答案 0 :(得分: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?

答案 1 :(得分:1)

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

如果散列图增加,则有一个非常昂贵的重新散列函数,因为该数组还必须增长到下一个2的幂。在这种情况下,每个存储桶都必须重新计算其索引。在这种情况下,将构建一个新的数组。这意味着不需要动态数据结构。

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

答案 2 :(得分:1)

来自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;

}