请说一说HashMap和Hashtable之间有哪些主要区别?

参考回答

HashMapHashtable 是 Java 中常用的两种 Map 实现类,它们都用于存储键值对,但它们之间存在一些关键区别:

  1. 线程安全性
    • HashMap 是非线程安全的,适合单线程环境。
    • Hashtable 是线程安全的,方法被 synchronized 修饰,适合多线程环境。
  2. 性能
    • 由于 HashMap 非线程安全,性能通常比 Hashtable 更高。
    • Hashtable 在多线程环境中由于同步开销,性能较低。
  3. 是否允许 null 键和 null
    • HashMap 允许一个 null 键和多个 null 值。
    • Hashtable 不允许 null 键和 null 值。
  4. 继承关系
    • HashMapMap 接口的实现类。
    • HashtableDictionary 类的子类(Dictionary 是一个过时的抽象类)。
  5. 初始容量和扩容机制
    • HashMap 的默认初始容量为 16,扩容时容量加倍。
    • Hashtable 的默认初始容量为 11,扩容时容量变为原来的 2 倍 + 1。
  6. 迭代器类型
    • 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 键有特殊逻辑。
  • 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.synchronizedMapConcurrentHashMap 使用。
  • Hashtable:
    • 在早期的多线程场景中使用,但已较少使用,通常用 ConcurrentHashMap 替代。

总结对比表

比较点 HashMap Hashtable
线程安全性 非线程安全 线程安全
性能 性能高,适合单线程 性能低,因同步开销大
是否允许 null 允许一个 null 键和多个 null 不允许 null 键或 null
迭代器类型 Iteratorfail-fast Enumerator,非 fail-fast
扩容机制 容量翻倍 容量变为 2 倍 + 1
使用场景 单线程或现代多线程 早期多线程场景,已较少使用

发表评论

后才能评论