Hashtable 与 HashMap 的区别笔记
在 Java 中,Hashtable
和 HashMap
都是用于存储键值对的哈希表实现类,继承自 Map
接口。虽然它们都提供了类似的功能,但它们在性能、线程安全性、支持的特性等方面存在一些显著的区别。了解这些差异有助于在开发过程中选择适合的集合类。
1. 线程安全性
1.1 Hashtable
Hashtable
是线程安全的。在多线程环境下,Hashtable
使用**同步(Synchronized)**机制来确保每次只有一个线程能访问它的内部数据。- 这种同步机制会使得
Hashtable
在并发环境下能够避免数据不一致问题,但也会显著影响性能。因为在同一时间只有一个线程能够访问它的任意部分,导致线程在进行读写操作时会发生阻塞。
1.2 HashMap
HashMap
是非线程安全的。在多线程环境中,多个线程对HashMap
进行修改时会产生不确定的结果,因此无法保证数据的一致性。- 如果需要在多线程环境中使用
HashMap
,通常会使用外部同步手段(如Collections.synchronizedMap()
或ReentrantLock
)来确保线程安全。
小结:
Hashtable
是线程安全的,但性能较差。HashMap
是非线程安全的,适合单线程或外部进行同步的情况。
2. 性能
2.1 Hashtable
由于 Hashtable
在每个操作上都进行同步,它在并发环境下会产生额外的性能开销。每次线程需要等待对 Hashtable
的访问,导致并发性能差。
2.2 HashMap
HashMap
由于不进行同步控制,它的性能通常优于 Hashtable
,尤其在单线程环境下,或者在使用外部同步机制时。没有同步开销,读取和写入速度较快。
小结:
Hashtable
的性能较差,适合较低并发的场景。HashMap
的性能更高,适合高并发的场景(需要外部同步)。
3. 空键和空值的支持
3.1 Hashtable
Hashtable
不允许键或值为null
。如果你尝试使用null
作为键或值插入到Hashtable
中,将会抛出NullPointerException
异常。
3.2 HashMap
HashMap
允许一个null
键和多个null
值。null
键会被存储在哈希表的第一个位置,而多个null
值则可以正常存储。
小结:
Hashtable
不允许null
键和值。HashMap
允许一个null
键和多个null
值。
4. 迭代器的不同
4.1 Hashtable
Hashtable
使用的是传统的Enumerator
迭代器,虽然Hashtable
的keySet()
和entrySet()
方法返回的是Iterator
,但它的底层实现是基于Enumerator
的。Enumerator
是在Vector
和Hashtable
中早期使用的一种迭代机制,但它已经过时,不支持remove()
操作,也没有在迭代时抛出ConcurrentModificationException
异常。
4.2 HashMap
HashMap
使用的是现代的Iterator
,它支持快速失败机制(fail-fast),即当集合结构在迭代过程中被修改时,会抛出ConcurrentModificationException
异常。- 迭代时,如果其他线程在修改
HashMap
,Iterator
会抛出异常。
小结:
Hashtable
使用的是过时的Enumerator
,不支持并发修改。HashMap
使用支持并发修改检查的Iterator
。
5. 扩容策略
5.1 Hashtable
Hashtable
在初次加载时,默认的初始容量为 11,负载因子为 0.75。扩容时会将容量扩大为原来的两倍,并且在扩容过程中会进行同步,导致性能进一步下降。- 扩容过程是全表锁定的,可能影响并发性能。
5.2 HashMap
HashMap
的默认初始容量为 16,负载因子为 0.75。与Hashtable
相比,HashMap
在扩容时的效率更高。扩容时会将容量扩大为原来的两倍。HashMap
的扩容也是线程不安全的,但它的性能优于Hashtable
,特别是在扩容操作中不需要全表加锁。
小结:
Hashtable
的扩容性能较差,尤其在高并发时。HashMap
的扩容性能更好,适合高并发场景。
6. 类的继承关系
6.1 Hashtable
Hashtable
继承自Dictionary
类,而Dictionary
是一个过时的类,已经不再推荐使用。Hashtable
实现了Map
接口,但由于它继承自Dictionary
,其设计较为老旧。
6.2 HashMap
HashMap
直接实现了Map
接口,并且不依赖于Dictionary
类,是现代 Java 集合框架的标准实现类。HashMap
是基于哈希表的实现,通常比Hashtable
更灵活和高效。
小结:
Hashtable
继承自过时的Dictionary
类。HashMap
直接实现Map
接口,符合现代 Java 集合设计。
7. 总结
特性 | Hashtable | HashMap |
---|---|---|
线程安全性 | 线程安全(使用同步) | 非线程安全 |
性能 | 由于同步机制,性能较差 | 性能较好,尤其在单线程下 |
空键和空值支持 | 不允许 null 键和值 | 允许 null 键和多个 null 值 |
迭代器 | 使用过时的 Enumerator ,不支持并发修改 | 使用现代的 Iterator ,支持并发修改检查 |
扩容机制 | 扩容时全表加锁,性能较差 | 扩容时不加锁,性能优越 |
类的继承关系 | 继承自 Dictionary 类 | 继承自 Map 接口,符合现代设计 |
结论
- 在现代 Java 开发中,
Hashtable
已经不再被推荐使用。由于其线程安全的机制,它的性能在高并发场景下不如HashMap
,而且使用过时的Dictionary
类。 HashMap
是更现代、更高效的选择,适用于大多数应用场景,尤其是在单线程环境或通过外部同步确保线程安全的情况下。如果需要线程安全的集合,推荐使用ConcurrentHashMap
,它提供了更好的并发性能和线程安全性。