请说一说HashMap和Hashtable之间有哪些主要区别?
参考回答
HashMap
和 Hashtable
是 Java 中常用的两种 Map
实现类,它们都用于存储键值对,但它们之间存在一些关键区别:
- 线程安全性:
HashMap
是非线程安全的,适合单线程环境。Hashtable
是线程安全的,方法被synchronized
修饰,适合多线程环境。
- 性能:
- 由于
HashMap
非线程安全,性能通常比Hashtable
更高。 Hashtable
在多线程环境中由于同步开销,性能较低。
- 由于
- 是否允许
null
键和null
值:HashMap
允许一个null
键和多个null
值。Hashtable
不允许null
键和null
值。
- 继承关系:
HashMap
是Map
接口的实现类。Hashtable
是Dictionary
类的子类(Dictionary
是一个过时的抽象类)。
- 初始容量和扩容机制:
HashMap
的默认初始容量为 16,扩容时容量加倍。Hashtable
的默认初始容量为 11,扩容时容量变为原来的 2 倍 + 1。
- 迭代器类型:
HashMap
使用的是Iterator
,是fail-fast
的,修改时会抛出ConcurrentModificationException
。Hashtable
使用的是Enumerator
和早期版本的Iterator
,不支持fail-fast
。
详细讲解与拓展
1. 线程安全性
- HashMap: 非线程安全,不能在多线程环境下直接使用。如果需要线程安全,可以使用:
Map<K, V> map = Collections.synchronizedMap(new HashMap<>());
或者使用
ConcurrentHashMap
作为线程安全替代。 -
Hashtable: 内部方法使用
synchronized
关键字实现同步,因此线程安全,但并发性能差。
示例:
Map<String, String> hashMap = new HashMap<>();
hashMap.put(null, "value1"); // 允许一个 null 键
hashMap.put("key2", null); // 允许 null 值
Map<String, String> hashTable = new Hashtable<>();
// hashTable.put(null, "value1"); // 抛出 NullPointerException
// hashTable.put("key2", null); // 抛出 NullPointerException
2. 性能
- 由于
HashMap
不涉及线程同步,其性能更高。 - 在单线程环境下,应优先使用
HashMap
。
性能对比示例:
long startTime = System.nanoTime();
Map<Integer, String> hashMap = new HashMap<>();
for (int i = 0; i < 100000; i++) {
hashMap.put(i, "Value " + i);
}
long endTime = System.nanoTime();
System.out.println("HashMap Time: " + (endTime - startTime));
startTime = System.nanoTime();
Map<Integer, String> hashTable = new Hashtable<>();
for (int i = 0; i < 100000; i++) {
hashTable.put(i, "Value " + i);
}
endTime = System.nanoTime();
System.out.println("Hashtable Time: " + (endTime - startTime));
3. 是否允许 null
- HashMap:
- 可以存储 1 个
null
键 和 多个null
值。 - 这是因为
HashMap
内部在处理键值对时,对于null
键有特殊逻辑。
- 可以存储 1 个
- Hashtable:
- 不允许存储
null
键或null
值,因为null
会导致同步操作时的异常。
- 不允许存储
示例:
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(null, "nullKey"); // 允许
hashMap.put("key", null); // 允许
Hashtable<String, String> hashTable = new Hashtable<>();
// hashTable.put(null, "nullKey"); // 抛出 NullPointerException
// hashTable.put("key", null); // 抛出 NullPointerException
4. 迭代器差异
- HashMap:
- 使用
Iterator
,具有fail-fast
特性。如果在迭代时修改了集合,会抛出ConcurrentModificationException
。
- 使用
- Hashtable:
- 使用
Enumerator
和早期版本的Iterator
,不具备fail-fast
特性。
- 使用
示例:
Map<String, String> hashMap = new HashMap<>();
hashMap.put("key1", "value1");
Iterator<String> iterator = hashMap.keySet().iterator();
hashMap.put("key2", "value2"); // 修改集合
// iterator.next(); // 抛出 ConcurrentModificationException
5. 扩容机制
- HashMap:
- 初始容量为 16,负载因子默认为 0.75。
- 扩容时,容量翻倍(
capacity * 2
)。
- Hashtable:
- 初始容量为 11,负载因子默认为 0.75。
- 扩容时,容量变为原容量的两倍加一(
capacity * 2 + 1
)。
6. 使用场景
- HashMap:
- 适用于单线程环境,或结合
Collections.synchronizedMap
或ConcurrentHashMap
使用。
- 适用于单线程环境,或结合
- Hashtable:
- 在早期的多线程场景中使用,但已较少使用,通常用
ConcurrentHashMap
替代。
- 在早期的多线程场景中使用,但已较少使用,通常用
总结对比表
比较点 | HashMap | Hashtable |
---|---|---|
线程安全性 | 非线程安全 | 线程安全 |
性能 | 性能高,适合单线程 | 性能低,因同步开销大 |
是否允许 null |
允许一个 null 键和多个 null 值 |
不允许 null 键或 null 值 |
迭代器类型 | Iterator ,fail-fast |
Enumerator ,非 fail-fast |
扩容机制 | 容量翻倍 | 容量变为 2 倍 + 1 |
使用场景 | 单线程或现代多线程 | 早期多线程场景,已较少使用 |