java - 为什么 HashMap 在达到不需要的 TREEIFY_THRESHOLD 值时会调整大小?
问题描述
我知道 HashMap 如何在内部工作。但是,在使用 TreeNode 实现检查 HashMap 代码时,我没有得到增加存储桶大小的目标,但直到存储桶大小达到MIN_TREEIFY_CAPACITY = 64 时才进行树化。
注意:我考虑过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并将所有元素转移到新选项卡。又是 32 到 64。我认为这是开销或不必要的。
那么这背后的目标是什么?TREEIFY_THRESHOLD
用in检查大小,putVal
但不做treeify直到它命中MIN_TREEIFY_CAPACITY
。
解决方案
两者,使用比平常更大的树或容量,都是处理冲突的措施。当有多个key映射到同一个bucket时,可以是以下场景之一(或它们的组合):
- 密钥具有不同的哈希码,但映射到同一个存储桶
- 密钥具有相同的哈希码,但实现
Comparable
- 密钥具有相同的哈希码并且不实现
Comparable
这两种方法都不能处理第三点。只有建造一棵树才能处理第二个。当我们遇到第一种情况时,扩展表可能会解决问题,如果确实如此,它的优点是仍然提供O(1)
查找并允许更有效的遍历(仅遍历数组),而树具有O(log n)
查找和较低效率的遍历,需要下降树结构。
问题是,分析场景,找出适用的解决方案以及扩展表格是否真的有帮助,这本身就需要时间。put
此外,当一个人花费分析的费用来解雇一个策略时,它不会得到回报,只是为了最终put
找到下一个适合另一个键的策略(毕竟,扩大表大小会影响整个桌子)。
因此,启发式用于适应 的可能性和典型用例HashMap
,而不仅仅是单个put
操作。请注意,对于较小的表大小,通过扩展解决存储桶冲突的机会更高,表大小为 16 意味着仅使用四位哈希码,而表大小为 32 意味着使用五位,多 25%。
我想,JDK 团队使用了对现实生活中的应用程序和库进行基准测试的常用方法来找到正确的折衷方案。
推荐阅读
- javascript - wkwebview 中的自定义字体使用
- android - 如何去除底部约束布局的额外空间
- sql-server - SQL Server 如果存在更新,没有“其他插入”
- android - Gradle Sync:等待其他线程完成获取分发永远不会结束
- nginx - 在 nginx 中为特定文件禁用 http 请求重定向
- sql - 如果可能,如何在 hive SQL 的分组阶段获得百分比差异?
- java - 当我运行我的 jframe 时,它总是显示“java.lang.NumberFormatException:空字符串”
- android - Android:从不在视图中的应用程序读取游戏手柄(游戏控制器)事件
- facebook-ios-sdk - Facebook SDK“购买”事件发送不正确
- ionic-framework - 我们如何显示在 ionic 2 中打开的默认轮日期选择器