【问题标题】:Some doubts regarding HashMap关于HashMap的一些疑惑
【发布时间】:2013-11-21 10:21:34
【问题描述】:
HashMap 以非常简单的方式实现,但它需要一个天才来理解它是如何实现的。所以,我在 java 文档中读到了 HashMap。我有一些关于HashMap的小问题:
- 我知道
HashMap 的默认容量是 16。在 java 文档中,他们给出了 默认初始容量 - 必须是 2 的幂。。这背后有什么具体原因吗?
- 如果我没记错的话,我知道
HashMap 是如何基于HashCode、Bucket 和LinkedList 工作的。那么HashMap 的大小是如何增加的。我的意思是如何管理存储桶大小和 LinkedList 大小。
- 这可能是个愚蠢的问题。当我们在
HashMap 中添加新元素时,它会根据 HashCode 直接访问该特定存储桶,而无需像在LinkedList 中那样移动。我对吗?另一件事是它在头部而不是尾部添加元素。这是什么原因。是否在桶内的LinkedList 的头部添加了新元素以避免尾部遍历。我的想法正确吗?
【问题讨论】:
标签:
java
collections
hashmap
【解决方案1】:
使用 2 的幂可简化实施并提高其性能。
例如。要从哈希码中找到一个桶,它可以使用 hash & (SIZE -1) 而不是 abs(hash) % SIZE
在您完全了解 HashMap 的工作原理之前,您将无法回答这个问题。如果地图的大小超过负载因子定义的给定阈值,例如如果负载因子为 0.75,它将在填充 75% 后重新调整地图大小。与ArrayList 等其他集合类类似,Java HashMap 通过创建一个大小为先前HashMap 大小两倍的新桶数组来重新调整自身大小,然后开始将每个旧元素放入该新桶数组中。此过程称为重新散列,因为它还应用散列函数来查找新的存储桶位置。
我们将每个新元素存储在链表的头部以避免尾部遍历,因此在调整链表中整个对象序列的大小时会反转,在此期间可能会出现无限循环。
在这里阅读更多:
【解决方案2】:
将容量设为 2 的幂的原因(我认为)主要是为了简化代码。有一点性能优势,但几乎可以忽略不计。
-
是这样的:
当您尝试添加新条目时,HashMap 会展开。它发生在(粗略地说)map.size() * load_factor > array.length 时。 (具体细节请参考代码。)
当 HashMap 展开时,数组的大小会增加一倍。 Java中的数组大小有一个硬性限制。之后,HasMap 的数组不会扩展。 (相反,你只会得到越来越长的哈希链......)
没有做任何事情来管理各个哈希链的长度。相反,当 HashMap 扩展时,每个旧链中的条目都被移动到扩展表中的相应链中。 (至少在最近的实现中,每个链节点都为条目保存了一个缓存的哈希值,因此在扩表过程中不需要重新评估哈希函数。)
基本上,是的,是的。新条目被添加到每个哈希链的开头,因为这样做是最有效的(时间和空间方面)。由于哈希链中元素的顺序没有任何意义,因此在链的尾部插入新条目是没有意义的。这也意味着在典型的 HashMap 实现中,展开表会颠倒哈希链条目的顺序。
请注意,HashMap 的实际行为和实际实现细节因 Java 的不同版本而异。唯一确定的方法是阅读您正在使用的 Java 版本的源代码。
【解决方案3】:
我假设两个要求的力量可以加快拾取桶的速度。如果你有 16 个桶,索引为 578123 什么的,你可以使用简单的 AND 来选择一个桶,而不必计算 578123 mod 16,这样比较慢。
HashMap 有一个负载因子,默认为 0.75。如果对象数量 > 存储桶数量 * 负载因子,则增加 HashMap 的容量以保持性能。我会假设它只是将存储桶的数量增加一倍并重新分配所有元素。
抱歉,我不确定我是否正确理解了这个问题。