HashMap
是 Java 集合框架中的一个核心类,它实现了 Map
接口,提供了基于哈希表的键值对存储。以下是 HashMap
的主要实现细节:
一、基本结构
-
数组(桶):
HashMap
内部维护了一个数组(通常称为“桶”或“table”),用于存储键值对。数组的每个元素都是一个链表(在 Java 8 及以后,如果链表长度超过一定阈值,链表会转换为红黑树以提高性能)。 -
链表/红黑树:在
HashMap
中,每个桶可能包含一个或多个键值对(取决于哈希冲突的程度)。在 Java 8 之前,桶中的元素以链表的形式存储。从 Java 8 开始,如果桶中的链表长度超过 8 且数组大小大于等于 64,链表将转换为红黑树,以优化查找性能。 -
哈希函数:
HashMap
使用一个哈希函数来计算键的哈希值,然后将哈希值映射到数组的一个索引上。理想情况下,哈希函数应该能够将不同的键均匀地分布到数组的各个桶中。
二、重要字段
DEFAULT_INITIAL_CAPACITY
:默认的初始容量(16)。MAXIMUM_CAPACITY
:允许的最大容量(2^30)。DEFAULT_LOAD_FACTOR
:默认的负载因子(0.75)。负载因子用于衡量哈希表在其容量自动增加之前可以达到多满。threshold
:扩容的阈值,当哈希表中的键值对数量超过这个值时,哈希表会进行扩容。size
:当前哈希表中键值对的数量。modCount
:记录HashMap
结构被修改的次数,用于快速失败(fail-fast)机制。
三、主要方法
- put(K key, V value):将键值对添加到哈希表中。如果键已经存在,则更新其值。
- get(Object key):根据键获取值。如果键不存在,则返回
null
。 - remove(Object key):根据键移除键值对。
- containsKey(Object key):检查哈希表中是否包含指定的键。
- containsValue(Object value):检查哈希表中是否包含指定的值。
- size():返回哈希表中键值对的数量。
- isEmpty():检查哈希表是否为空。
- clear():清空哈希表中的所有键值对。
四、扩容机制
当哈希表中的键值对数量超过扩容阈值(threshold
)时,HashMap
会自动扩容。扩容过程包括创建一个新的数组(容量通常是原数组的两倍),然后重新计算每个键的哈希值并将键值对重新插入到新的数组中。
五、线程安全性
HashMap
不是线程安全的。如果多个线程同时访问一个 HashMap
实例而没有适当的同步机制,可能会导致数据不一致和竞争条件。在需要线程安全的场景中,可以使用 ConcurrentHashMap
或在访问 HashMap
时使用外部同步。
六、性能特点
- 时间复杂度:在理想情况下(即哈希函数分布均匀且没有哈希冲突),
HashMap
的get
和put
操作的时间复杂度都是 O(1)。然而,在存在哈希冲突的情况下,时间复杂度可能会退化为 O(n)(n 是桶中链表或红黑树的长度)。 - 空间复杂度:
HashMap
的空间复杂度主要取决于其容量和负载因子。随着键值对数量的增加,HashMap
可能会自动扩容以容纳更多的键值对。
综上所述,HashMap
是一个高效的键值对存储结构,适用于大多数需要快速查找和插入操作的场景。然而,在需要线程安全或有序键存储的场景中,可能需要考虑其他数据结构或同步机制。