我最近才知道在Java 8哈希映射中使用二叉树而不是链表,并使用哈希代码作为分支因子。我知道在高冲突的情况下,查找将减少为O(log n) O(n)通过使用二叉树。我的问题是它真正做了什么好处,因为摊销的时间复杂度仍然是O(1)并且可能如果你强制通过提供相同的哈希码来存储同一个桶中的所有条目所有的钥匙,我们都可以看到显着的时差,但没有一个人能够做到这一点。
二叉树比单链表使用更多的空间,因为它存储左右节点。当除了一些虚假的测试用例之外,当时间复杂度完全没有改善时,为什么会增加空间复杂度。
答案 0 :(得分:22)
这主要是与安全相关的变化。虽然在正常情况下很少有可能发生很多冲突,如果哈希密钥来自不受信任的来源(例如从客户端收到的HTTP头名称),那么可能并且不是很难专门设计输入,因此生成的密钥将具有相同的哈希码。现在,如果您执行许多查找,您可能会遇到拒绝服务。看起来野外有很多代码容易受到这种攻击,因此决定在Java端解决这个问题。
有关更多信息,请参阅JEP-180。
答案 1 :(得分:17)
你的问题包含一些错误的前提。
Map
的大小应该合理,因此不能任意提高数组大小以避免存储桶冲突。甚至理论上的限制是阵列大小最大可达2³¹,而有2 2,3可能的哈希码。String
是一个明显的例子,但即使带有Point
个int
值的Long
或普通Comparable
密钥也会产生不可避免的哈希冲突。因此,它们可能比您想象的更常见,并且在很大程度上取决于用例。Comparable
时,才会使用其自然顺序。您可能在Web上找到的示例故意使用相同的对象哈希代码来演示List<string> folders = new List<string>();
string folderResult = string.Join(",", ParallelMeasurements
.Select(parallelMeasurement => parallelMeasurement.Item1.Substring(0, parallelMeasurement.Item1.IndexOf(".")))
.Where(folder => folders.Contains(folder))
.Select(folder =>
{
folders.Add(folder);
return folder;
}));
string valueResult = string.Join(",", folders
.Select(folder => ParallelMeasurements
.Where(parallelMeasurement => parallelMeasurement.Item1.Substring(0, parallelMeasurement.Item1.IndexOf(".")) == folder))
.Select(views => views.Sum(view => view.Item2.TotalSeconds)));
实现的使用,否则将无法显示。他们触发的只是实施的最后手段。