Hashtable 与 HashMap 的区别笔记

在 Java 中,HashtableHashMap 都是用于存储键值对的哈希表实现类,继承自 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 迭代器,虽然 HashtablekeySet()entrySet() 方法返回的是 Iterator,但它的底层实现是基于 Enumerator 的。
  • Enumerator 是在 VectorHashtable 中早期使用的一种迭代机制,但它已经过时,不支持 remove() 操作,也没有在迭代时抛出 ConcurrentModificationException 异常。

4.2 HashMap

  • HashMap 使用的是现代的 Iterator,它支持快速失败机制(fail-fast),即当集合结构在迭代过程中被修改时,会抛出 ConcurrentModificationException 异常。
  • 迭代时,如果其他线程在修改 HashMapIterator 会抛出异常。
小结:
  • 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,它提供了更好的并发性能和线程安全性。
相关推荐
cui_hao_nan21 分钟前
JVM——如何对java的垃圾回收机制调优?
java·jvm
熟悉的新风景2 小时前
springboot项目或其他项目使用@Test测试项目接口配置-spring-boot-starter-test
java·spring boot·后端
心平愈三千疾2 小时前
学习秒杀系统-实现秒杀功能(商品列表,商品详情,基本秒杀功能实现,订单详情)
java·分布式·学习
玩代码2 小时前
备忘录设计模式
java·开发语言·设计模式·备忘录设计模式
BUTCHER53 小时前
Docker镜像使用
java·docker·容器
岁忧3 小时前
(nice!!!)(LeetCode 面试经典 150 题 ) 30. 串联所有单词的子串 (哈希表+字符串+滑动窗口)
java·c++·leetcode·面试·go·散列表
艾莉丝努力练剑4 小时前
【数据结构与算法】数据结构初阶:详解顺序表和链表(四)——单链表(下)
c语言·开发语言·数据结构·学习·算法·链表
珊瑚里的鱼4 小时前
第十三讲 | map和set的使用
开发语言·c++·笔记·visualstudio·visual studio
逑之4 小时前
C++笔记1:命名空间,缺省参数,引用等
开发语言·c++·笔记
LJianK14 小时前
Java和JavaScript的&&和||
java·javascript·python