HashMap
是 Java 中广泛使用的基于哈希表的 Map 接口实现。它存储键值对,并且允许使用 null
作为键和值。以下是 HashMap
的一些关键概念和内部工作原理:
哈希表基础
- 哈希函数:
HashMap
使用哈希函数来计算每个键的哈希码,这个哈希码决定了键值对(Entry
)在HashMap
内部数组中的存储位置(即索引)。 - 数组:所有的
Entry
对象被存储在一个数组中,这个数组的大小是动态的,并且当HashMap
中的Entry
数量达到一个阈值时会重新进行散列(即rehash
),这涉及到扩展数组大小和重新定位现存的Entry
对象。
键值对存储
- 键值对:每个键值对在
HashMap
中以Map.Entry
对象的形式存在。Entry
包含键、值和指向下一个Entry
的引用(在解决哈希冲突时使用)。
哈希冲突和链表
- 哈希冲突:当两个或多个键的哈希码相同时,会发生哈希冲突。为了解决哈希冲突,
HashMap
使用链表来存储具有相同哈希码的Entry
对象。这意味着具有相同哈希码的Entry
对象会被链接在一起。
动态数组和链表转换为红黑树
- 扩容和
rehash
:当HashMap
中的Entry
数量达到容量和负载因子(load factor
)的乘积时,HashMap
会进行扩容(通常是当前容量的两倍)并rehash
所有的Entry
对象。 - 红黑树:在 Java 8 中,如果一个桶内的链表长度超过一定阈值(默认为 8),这个链表会被转换成红黑树以提高搜索效率。这减少了在最坏情况下操作的时间复杂度从
O(n)
降低到O(log n)
。
主要方法
put(Key k, Value v)
:添加键值对到HashMap
。如果键已经存在,其对应的值将被新的值替换。get(Object key)
:根据提供的键返回对应的值。如果键不存在,则返回null
。remove(Object key)
:删除指定键的键值对,并返回其对应的值。
迭代
- 迭代顺序:
HashMap
的迭代顺序是不确定的,因为它依赖于键的哈希码和HashMap
的容量。 entrySet()
、keySet()
和values()
:HashMap
提供了这几个方法来迭代键值对、键的集合或值的集合。
并发和线程安全
- 线程安全:
HashMap
不是线程安全的。如果需要在多线程环境中使用HashMap
,可以使用Collections.synchronizedMap
方法来包装HashMap
或者使用ConcurrentHashMap
。
以上是 HashMap
的一些关键原理和特性。理解这些原理有助于更有效地使用 HashMap
,并且在面对性能问题时能够做出正确的优化决策。