从我在stackoverflow和其他网站上阅读的内容开始。 Java使用链表进行哈希冲突解决。
这将保证插入,获取和删除的最坏情况下的O(n)复杂度。
为什么Java不使用自平衡BST(如AVL,Red Black等)来保证在插入,获取和删除的最坏情况下的O(log n)复杂度?
答案 0 :(得分:4)
Java 8 确实,如果链接链变得足够大。
但是,对于少量元素,它会增加大量内存和性能开销。链接列表对于非常少量的元素非常有效,这正是您在99%的情况下对散列桶的期望。另外,定义二叉树应该如何排序是非常明显的,并且当元素为Comparable
时必须特殊情况,按未使用的哈希码位进行排序......它会变得多毛。
答案 1 :(得分: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的实现,我们可以看到每个桶都是一个非常简单的链表 - 比java.util.LinkedList简单,它实际上是一个双向链表。
根据评论,当列表增长到一定大小时,它会转换为树。很难确切地告诉HashMap.TreeNode发生了什么,因为代码不是完全自我描述的,但它似乎是一个简单的红黑BST。