【问题标题】:Java's HashMap collision resolutionJava的HashMap冲突解决
【发布时间】:2016-11-05 08:31:49
【问题描述】:

所以从我在 stackoverflow 和其他网站上读到的内容来看。 Java 使用链表解决哈希冲突。

这将保证在插入、获取和删除的最坏情况下的复杂度为 O(n)。

为什么 Java 不使用自平衡 BST(如 AVL、Red Black 等)来保证最坏情况下插入、获取和删除的 O(log n) 复杂度?

【问题讨论】:

  • 惊喜:Java 8 使用平衡树来处理冲突:javarevisited.blogspot.sg/2016/01/…
  • 主要原因是HashMap不要求其元素类型为Comparable。但是新的 Java 8 实现将在运行时检查元素是否可比较,如果是,则使用平衡树。这是相当棘手的(仅仅因为一个元素可以与它自己的类型进行比较并不意味着它可以与 HashMap 中的所有其他键类型进行比较)——这可能是为什么不早点这样做的原因。

标签: java hashmap


【解决方案1】:

Java 8 可以,如果链接链足够大。

但是,对于少量元素,它会增加显着的内存和性能开销。链表对于非常少量的元素确实非常有效,这正是您在 99% 的情况下对哈希桶的期望。此外,定义二叉树应该如何排序并不是显而易见的,当元素为Comparable 时,必须按照未使用的哈希码位排序……这很麻烦。

【讨论】:

  • 哦,哇,我没想过这么深。谢谢,现在很有意义!
【解决方案2】:

大多数情况下,存储桶中的项目数量很少;通常为零或一。在这些情况下,一个简单的哈希桶结构能够保证 O(1);在某些次优边缘情况下,O(log n) BST 可能会切断时间,但性能增益在最好的情况下可以忽略不计,在最坏的情况下是负的。还有很大的内存开销。 Java 8 确实努力检测链表何时不再是最优的并转换为 BST;但是,如果这种行为频繁发生,则表明哈希和 HashMap 使用不正确。

在阅读 JDK 的源代码时,可以获得很多实现细节。以下是 Oracle 的 java.util.HashMap 顶部的简短摘录:

/*
 * Implementation notes.
 *
 * This map usually acts as a binned (bucketed) hash table, but
 * when bins get too large, they are transformed into bins of
 * TreeNodes, each structured similarly to those in
 * java.util.TreeMap. Most methods try to use normal bins, but
 * relay to TreeNode methods when applicable (simply by checking
 * instanceof a node).  Bins of TreeNodes may be traversed and
 * used like any others, but additionally support faster lookup
 * when overpopulated. However, since the vast majority of bins in
 * normal use are not overpopulated, checking for existence of
 * tree bins may be delayed in the course of table methods.
 * [...]

查看HashMap#getNode和HashMap.Node的实现,我们可以看到每个bucket开头都是一个非常简单的链表——比java.util.LinkedList简单,实际上是一个双向链表。

根据评论,当列表增长到一定大小时,它会转换为树。很难准确判断 HashMap.TreeNode 中发生了什么,因为代码不是完全自描述的,但它似乎是一个简单的红黑 BST。

【讨论】:

  • 谢谢!我想我在看 Java 7 和更早的 cmets。 :)
猜你喜欢
  • 2013-11-10
  • 2012-11-25
  • 1970-01-01
  • 2016-04-14
  • 2012-12-23
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2020-09-22
相关资源
最近更新 更多