`HashMap` 和 `ConcurrentHashMap` 都是 Java 中用来存储键值对的集合类,但它们有不同的设计目标,适用于不同的场景。以下是它们的详细对比和介绍。
### 1. `HashMap` 概述
`HashMap` 是基于哈希表的非同步集合类,主要用于在单线程环境下存储键值对。
#### 特性:
- **线程不安全**:`HashMap` 不是线程安全的,多个线程同时操作时可能会导致数据不一致的问题。如果需要在多线程环境中使用,可以手动进行同步或者使用同步包装器(例如 `Collections.synchronizedMap()`)。
- **允许 `null` 键和值**:`HashMap` 允许一个 `null` 键和多个 `null` 值。
- **性能优越**:由于没有同步开销,在单线程环境下,`HashMap` 的性能非常好。
- **无序**:元素的存储顺序是无序的,不能保证插入的顺序。
#### 示例代码:
```java
import java.util.HashMap;
public class HashMapExample {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put(null, 100); // 允许null键
map.put("Charlie", null); // 允许null值
// 遍历HashMap
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
}
}
```
### 2. `ConcurrentHashMap` 概述
`ConcurrentHashMap` 是 Java 5 引入的线程安全集合类,主要用于高并发环境下存储键值对。它通过分段锁(Java 8 之后采用 CAS 和少量锁)来确保线程安全,同时提供比 `Hashtable` 更高效的并发性能。
#### 特性:
- **线程安全**:`ConcurrentHashMap` 使用了一种更细粒度的锁机制(分段锁或 CAS 操作),保证线程安全且并发性能优越。在多线程下允许并发读写,而不会出现 `HashMap` 的数据一致性问题。
- **不允许 `null` 键和值**:与 `HashMap` 不同,`ConcurrentHashMap` 不允许 `null` 键或 `null` 值,插入 `null` 会抛出 `NullPointerException`。
- **高效并发**:通过分段锁或无锁(Java 8 之后的 CAS),多个线程可以同时访问不同的分段,降低了锁争用,提高了并发性能。
- **性能较好**:在高并发环境下,`ConcurrentHashMap` 的性能优于 `Hashtable`,因为它并不对整个 Map 加锁。
#### 示例代码:
```java
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
// map.put(null, 100); // 不允许null键
// map.put("Charlie", null); // 不允许null值
// 遍历ConcurrentHashMap
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
}
}
```
### 3. `HashMap` 与 `ConcurrentHashMap` 对比
| 特性 | `HashMap` | `ConcurrentHashMap` |
|--------------------------|------------------------------------|------------------------------------|
| **线程安全** | 否,线程不安全 | 是,线程安全 |
| **允许 `null` 键和值** | 允许 `null` 键和 `null` 值 | 不允许 `null` 键和值 |
| **同步机制** | 无 | 分段锁或 CAS(无锁) |
| **性能** | 在单线程环境下性能更优 | 在多线程环境下性能优越 |
| **适用场景** | 单线程环境或非并发操作场景 | 多线程环境下的并发操作 |
| **迭代器行为** | 迭代期间会抛出 `ConcurrentModificationException` | 迭代期间不抛异常,支持弱一致性 |
| **扩容机制** | 负载因子达到 0.75 时扩容 | 负载因子达到 0.75 时扩容,线程安全 |
### 4. Java 8 对 `HashMap` 和 `ConcurrentHashMap` 的优化
- **`HashMap` 的优化**:在 Java 8 之前,`HashMap` 使用链表来处理哈希冲突,而在 Java 8 中,当链表长度超过一定阈值时,它会转换为红黑树结构来优化查询和插入操作。
- **`ConcurrentHashMap` 的优化**:Java 8 中的 `ConcurrentHashMap` 摒弃了原有的分段锁机制,而是采用了一种基于 CAS 和少量锁的方式来进一步提升并发性能。
### 5. `ConcurrentHashMap` 的分段锁原理(Java 7 之前)
在 Java 7 及之前的版本中,`ConcurrentHashMap` 使用了分段锁(Segmented Locking)机制。它将整个 `ConcurrentHashMap` 分为多个 Segment,每个 Segment 都是一个小的 `Hashtable`。多个线程可以同时访问不同的 Segment,实现并发访问。
在 Java 8 中,分段锁的实现被简化,使用 CAS 和少量的锁来优化性能,减少了锁的使用,提高了并发性能。
### 6. 总结
- **`HashMap`** 是在单线程环境中使用的高效集合类,允许 `null` 键和值,但在多线程环境中不安全。
- **`ConcurrentHashMap`** 是为高并发设计的线程安全集合类,不允许 `null` 键和值,适用于多线程环境,并提供更高效的并发性能。
- 在多线程场景下,**`ConcurrentHashMap`** 是替代 **`Hashtable`** 和 **同步化 `HashMap`** 的最佳选择。