我试图了解如何在Java中实现HashMap。我决定尝试理解该课程的每一行(代码和评论),显然我很快就遇到了阻力。以下代码片段来自HashMap类,并讨论泊松分布:
- 理想情况下,在随机hashCodes下,频率为
- 箱中的节点遵循泊松分布
- (http://en.wikipedia.org/wiki/Poisson_distribution)with
- 默认调整大小的平均值约为0.5
- 阈值为0.75,但由于
而存在较大差异- 调整粒度。忽略差异,预期
- 列表大小k的出现是(exp(-0.5)* pow(0.5,k)/
- 阶乘(K))。第一个值是: *
- 0:0.60653066
- 1:0.30326533
- 2:0.07581633
- 3:0.01263606
- 4:0.00157952
- 5:0.00015795
- 6:0.00001316
- 7:0.00000094
- 8:0.00000006
- 更多:不到千万分之一
我是数学界的普通人,必须先了解泊松分布是什么。感谢简单的视频向我解释。
现在,即使了解了如何使用Poisson计算概率,我也无法理解上述内容。
有人可以用更简单的语言解释一下,如果可能,请举例说明吗?这将使我的任务更有趣。
提前致谢
答案 0 :(得分:6)
根据要插入的元素的hashCode,将HashMap组织为“桶”数组。每个存储桶(默认情况下)是元素的链接列表。每个桶只有很少的元素(理想情况下,最多只有一个),因此找到一个特定元素只需要很少搜索链表。
举一个简单的例子,假设我们有一个容量为4的HashMap和一个0.75的加载因子(默认值),这意味着它在调整大小之前最多可以容纳3个元素。元素到桶中的理想分布看起来像这样:
bucket | elements
-------+---------
0 | Z
1 | X
2 |
3 | Y
因此可以立即找到任何元素,而无需在存储桶中进行任何搜索。另一方面,元素分布非常差,如下所示:
bucket | elements
-------+---------
0 |
1 | Z -> X -> Y
2 |
3 |
如果所有元素都碰巧散列到同一个存储桶中,则会发生这种情况,因此搜索元素Y将需要遍历链接列表。
这可能看起来不是什么大问题,但是如果你有一个容量为10,000个元素的HashMap,并且链表上的一个存储桶中有7,500个元素,那么搜索特定元素会降低到线性搜索时间 - - 这是使用HashMap试图避免的。
一个问题是用于将元素分配到存储桶的hashCode由对象本身决定,而对象的hashCode实现并不总是非常好。如果hashCode不是很好,那么元素可以在某些桶中聚集,并且HashMap将开始表现不佳。
代码中的评论是关于每个桶中出现不同长度的链表的可能性。首先,它假设hashCodes是随机分布的 - 情况并非总是如此! - 我认为它还假设HashMap中的元素数量是桶数量的50%。根据这些假设,根据Poisson分布,60.6%的桶将是空的,30.3%将具有一个元素,7.5%将具有两个元素,1.2%将具有三个元素,等等。
换句话说,鉴于那些(理想的)假设,每个桶中的链表通常都很短。
在JDK 8中,有一种优化方法可以将链表转换为高于某个阈值大小的树,这样在最坏的情况下至少性能会降低到O(log n)而不是O(n)。问题是,应选择什么值作为阈值?这就是讨论的全部内容。当前阈值TREEIFY_THRESHOLD是8.再次,在这些理想假设下,具有长度为8的链表的桶将仅出现0.000006%的时间。因此,如果我们得到一个很长的链表,那么显然不理想!!例如,它可能意味着存储的对象具有特别糟糕的hashCode,因此HashMap必须从链表切换到树,以避免过度的性能下降。
带有相关评论的源文件的链接在这里:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/jdk8-b119/src/share/classes/java/util/HashMap.java
答案 1 :(得分:2)
接受的答案很棒,但我只是想填写为什么特别适合使用泊松分布,因为在阅读那段代码时我有完全相同的问题。
如果我们将固定数量的项k
插入到固定数量的存储区n
中,那么固定存储区中的项目数应遵循Binomial Distribution
k
试验和成功的可能性1 / n
。这很容易看到;如果哈希值是随机的,则每个项目都会以概率1 / n
放入我们的存储桶中,并且有k
项。
当k
很大且二项分布的均值很小时,一个好的近似值是Poisson Distribution,具有相同的均值。
在这种情况下,均值为k / n
,哈希表的加载因子。取0.5的平均值是合理的,因为在调整大小之前,表允许负载因子最多为0.75,因此将使用该表
负载系数大约为0.5。