----本文是根据 ChatGPT3生成,仅供参考
HashMap
是 Java 集合框架中的一个常用类,用于存储键值对。它具有快速的查找、插入和删除操作效率。在理解 HashMap
的底层源码时,我们主要关注以下几个方面:
1. 基本结构
HashMap
底层基于数组和链表的组合实现,从 Java 8 开始还引入了红黑树来优化性能。其核心结构是一个数组,数组中的每个元素称为桶(Bucket)。每个桶存储一个链表或红黑树,以解决哈希冲突。
2. 哈希函数
HashMap
使用键的 hashCode()
方法来计算哈希值,进而确定元素在数组中的存储位置。具体做法是对哈希值进行处理,然后与数组长度取模来定位索引:
int hash = key.hashCode();
int index = (n - 1) & hash;
3. 解决哈希冲突
哈希冲突是指不同的键具有相同的哈希值,导致它们被分配到同一个桶中。HashMap
采用以下两种方式解决哈希冲突:
- 链表法:冲突的元素会被存储在链表中。新元素会被插入到链表的头部。
- 红黑树(Java 8 及之后):当单个桶中链表长度超过阈值(默认 8)时,链表会转换为红黑树,以提高查找效率。
4. 扩容机制
HashMap
的默认初始容量为 16,负载因子为 0.75。负载因子决定了 HashMap
何时扩容。当存储的元素数量超过 容量 * 负载因子
时,HashMap
会进行扩容,将容量扩大为原来的两倍,并重新分配元素位置。这是一个代价较高的操作,因此合理设置初始容量可以减少扩容的次数。
5. 插入(put)操作
当调用 put(key, value)
方法时:
- 计算键的哈希值,定位到数组索引。
- 如果桶为空,则直接插入新的
Node
(键值对)。 - 如果桶不为空(发生哈希冲突),则在链表或红黑树中查找:
- 如果找到相同的键,则更新其值。
- 如果未找到相同的键,则将新的
Node
插入链表尾部或红黑树中。
- 检查是否需要转换链表为红黑树或进行扩容。
6. 查找(get)操作
当调用 get(key)
方法时:
- 计算键的哈希值,定位到数组索引。
- 如果桶为空,返回
null
。 - 如果桶不为空,遍历链表或红黑树,查找匹配的键,返回对应的值。
7. 删除(remove)操作
当调用 remove(key)
方法时:
- 计算键的哈希值,定位到数组索引。
- 遍历桶中的链表或红黑树,找到匹配的键。
- 删除该节点,调整链表或红黑树的结构。
8. 线程安全性
HashMap
是非线程安全的。如果在多线程环境中需要使用线程安全的 Map
,可以使用 Collections.synchronizedMap
或 ConcurrentHashMap
。
总结
HashMap
通过数组、链表、红黑树相结合的方式实现快速的查找、插入和删除操作。它主要依赖哈希函数来确定存储位置,并通过链表和红黑树来解决哈希冲突。了解其底层实现有助于我们在实际开发中更好地使用它,避免一些潜在问题,如哈希冲突过多导致的性能下降