hashmap 中的 bucket 到底是啥?
Posted
技术标签:
【中文标题】hashmap 中的 bucket 到底是啥?【英文标题】:What exactly is bucket in hashmap?hashmap 中的 bucket 到底是什么? 【发布时间】:2016-10-23 22:01:47 【问题描述】:最近,在一次采访中,有人问我,hashmap 中的桶到底是什么?无论是数组还是数组列表还是什么?
我很困惑。我知道哈希图由数组支持。那么我可以说bucket是一个容量为16的数组,开始存储hashcode,哪些链表有起始指针?
我知道哈希图在内部是如何工作的,只是想知道存储桶在数据结构方面到底是什么。
【问题讨论】:
你需要阅读这篇文章 (***.com/questions/6493605/…) @JonnyHenly :我特别想知道桶是什么?在提到的问题中,更多的是处理哈希码和哈希图的实现。所以我不认为我的问题是重复的。问题可能相似,但他们寻找的答案却不同。 【参考方案1】:Buckets 确切地说 是一个节点数组。所以单桶是 java.util.HashMap.Node 类的一个实例。每个 Node 都是类似于 LinkedList 的数据结构,或者可能类似于 TreeMap(从 Java 8 开始),HashMap 自行决定哪个对性能更好——将存储桶保留为 LinkedList 或 TreeMap。只有在 hashCode() 函数设计不佳的情况下才会选择 TreeMap,此时大量条目将放置在单个存储桶中。 看看桶在 HashMap 中的样子:
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
【讨论】:
【参考方案2】:Buckets 基本上是一种用于操作系统分页算法的数据结构。使用非常通俗的语言。
代表特定哈希码的对象被存储在该桶中。(基本上您可以将链表数据结构的标题视为以桶的形式表示的哈希码值)
对象的引用被存储在链表中,链表的头部代表哈希码的值。
JVM 创建它们,大小取决于 JVM 分配的内存。
【讨论】:
【参考方案3】:我希望这可以帮助您更好地理解哈希映射的实现。
【讨论】:
【参考方案4】:不,存储桶是您所指的数组中的每个元素。在早期的 Java 版本中,每个存储桶都包含一个 Map 条目的链接列表。在新的 Java 版本中,每个存储桶都包含条目的树结构或条目的链接列表。
来自 Java 8 中的实现说明:
/*
* 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一开始的容量是16,所以当时它创建了一个16空间的数组,并且它的每个元素都称为一个桶。? @dgupta3091 是的,尽管在 Java 8 实现中,数组是延迟创建的(即仅当第一个条目放入 HashMap 时)。 @JonnyHenly 我刚刚检查了 Java 8 中的实现,并且没有一个构造函数初始化数组。它仅由resize()
(如果数组为空,将由put
调用)和readObject(java.io.ObjectInputStream s)
(反序列化)初始化。
@Eran 有道理,我忘了设置初始容量的主要原因是尽量减少重新哈希操作的次数。我只是觉得懒惰地创建数组很奇怪,例如——你在加载方法中创建了一个初始容量相当大的哈希图,认为这需要一些时间。然后稍后你会发现第一次调用 put 比加载函数花费的时间更长。也许我的逻辑是错误的,我很累。感谢您回复我的评论。
@Konstantin 是的,每个桶都是java.util.HashMap.Node
的一个实例以上是关于hashmap 中的 bucket 到底是啥?的主要内容,如果未能解决你的问题,请参考以下文章
HashMap 的迭代器是快速失败的,而 HashTable 的枚举器不是,你到底是啥意思?