java - 为什么 HashMap 在达到不需要的 TREEIFY_THRESHOLD 值时会调整大小?

标签 java java-8 hashmap

我知道 HashMap 内部是如何工作的。但是,在使用 TreeNode 实现检查 HashMap 代码时,我没有得到存储桶大小增加背后的目标,但直到存储桶大小达到 MIN_TREEIFY_CAPACITY = 64

之前,我没有得到 treeify 的目标。

注意:我考虑过 Map m = new HashMap(); 因此默认大小为 16。

默认值。

static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;

HashMap#putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)

我从 putVal 方法中提取了几行。

else {
    for (int binCount = 0; ; ++binCount) {
        if ((e = p.next) == null) {
            p.next = newNode(hash, key, value, null);
            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                treeifyBin(tab, hash);
            break;
        }
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            break;
        p = e;
    }
}

因此,每当 binCount 达到 7 时,它就会调用 treeifyBin(tab, hash); 现在让我们按照方法 treeifyBin 中的代码进行操作。

HashMap#treeifyBin(Node[] tab, int hash)

final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        TreeNode<K,V> hd = null, tl = null;
        ....
    }
}

为什么? 在此方法中,首先 IF 检查当前 tab 大小是否小于 MIN_TREEIFY_CAPACITY = 64,然后调用 resize().它在内部将 tab 大小从默认的 16 增加到 32 并将所有元素转移到新的 tab。再次32 到 64。我认为这是开销或不必要的。

那么这背后的目标是什么?在 putVal 中使用 TREEIFY_THRESHOLD 检查大小,但在达到 MIN_TREEIFY_CAPACITY 之前不执行 treeify

最佳答案

无论是使用树还是比平常更大的容量,都是处理冲突的措施。当有多个key映射到同一个bucket时,可能是以下场景之一(或它们的组合):

  1. 键具有不同的哈希码,但映射到同一个存储桶
  2. 键具有相同的哈希码,但实现 Comparable
  3. key 具有相同的哈希码,并且未实现 Comparable

这两种方法都无法解决第三点。只有 build 一棵树才能解决第二个问题。当我们遇到第一种情况时,扩展表可能会解决问题,如果确实如此,它的优点是仍然提供 O(1)查找并允许更有效的遍历(只需迭代数组),而树有 O(log n)查找和遍历效率较低,需要沿树结构下降。

问题是,分析场景、找出适用的解决方案以及扩展表是否确实有帮助,这本身就需要时间。此外,当单个 put 时,它不会得到返回。花费分析的费用来驳回策略,只是为了得到下一个 put寻找适合另一个key的策略(毕竟扩大表大小对整个表有影响)。

因此,我们使用启发式方法来适应 HashMap 的可能性和典型用例。 ,不仅仅是一个put手术。请注意,对于较小的表大小,通过扩展解决存储桶冲突的机会较高,表大小为 16 意味着仅使用 4 位哈希码,而表大小为 32 意味着使用 5 位,多出 25%。

我想,JDK 团队使用了对现实应用程序和库进行基准测试的常用方法来找到正确的权衡。

关于java - 为什么 HashMap 在达到不需要的 TREEIFY_THRESHOLD 值时会调整大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58987907/

相关文章:

java - 在 Java 中修改 .txt 文件

java - intellij左上角的"search for"

java - 告诉编译器一个 <Object> 等价于它想要的 <?>

java - 保持最后一个对象处于 Activity 状态并按日期停用较旧的对象

c++ - 稳定排序C++ HashMap -保留相等元素的插入顺序

java - 为什么有时会跳过 input.next() ?

java - 使用普通 W3C 文档和 Apache Batik SVG 光栅器

generics - Java 8 [无法推断类型变量]问题

c++ - 在 C++ 中初始化模板时将函数传递给模板对象

java - 从 JAVA 中的多维 JSON 映射检索数据