在 Java 的集合框架中,HashMap 和 Hashtable 都是用于存储键值对(Key-Value pairs)的 Map 接口的重要实现。它们看似功能相似,但在实际开发中,HashMap 几乎已经完全取代了 Hashtable。
首先我们要知道,Hashtable 是一个古老的、线程安全的类,但由于其性能开销和设计上的保守,在现代 Java 开发中已被视为遗留类(Legacy Class)。而 HashMap 是设计更优、性能更高、更现代化的首选。在需要线程安全的场景下,也有远比 Hashtable 更好的替代方案。
↓↓↓↓↓↓↓↓↓
一、线程安全性 (Thread Safety)
·Hashtable 是线程安全的。其几乎所有公共方法(如 put, get, size)都使用了 synchronized 关键字进行修饰,这意味着每个方法在执行时都会锁定整个对象实例。在多线程环境下,同一时间只有一个线程能操作这个 Hashtable,从而保证了数据的一致性。
但是,随之而来的是这种粗粒度的锁机制导致了巨大的性能开销。即使在单线程环境下,也会承担不必要的同步代价。
·HashMap 是非线程安全的。它的方法没有同步措施。这意味着在多线程环境下,如果多个线程同时修改一个 HashMap(例如添加或删除元素),可能会导致数据不一致、循环链表等问题。
与此同时,没有了同步的开销,HashMap 在单线程环境下的性能远高于 Hashtable。
二、对 Null 键和 Null 值的支持
·HashMap 明确允许使用 一个 null 键 和 任意数量的 null 值。这是因为 HashMap 在实现上对 null 键做了特殊处理,将其哈希值定为 0。
·Hashtable 不允许 null 键或 null 值。如果你尝试插入 null,会直接抛出 NullPointerException。这是其设计上的一个限制,因为在其线程安全的方法中,如果遇到 null,需要做额外的检查和错误处理,设计者选择了直接禁止。
三、性能
由于线程安全性的实现方式不同,性能差异非常明显。 在单线程程序中,HashMap 的速度远超 Hashtable。即使在多线程程序中,Hashtable 的效率也通常不高,因为激烈的锁竞争会成为系统瓶颈。
那么,现代开发中如何选择呢?
※单线程环境 ·毫无疑问选择 HashMap。性能更好,功能更全面。
※多线程环境: ·绝对不要使用 Hashtable!它不是最优解。 ·首选:使用 ConcurrentHashMap 类。它位于 java.util.concurrent 包下,采用了更细粒度的锁机制(在 JDK 7 是分段锁,JDK 8 及以后是 CAS + synchronized 锁单个桶),提供了更高的并发性能和可伸缩性。它是为高并发场景而生的现代类。 ·使用 Collections.synchronizedMap(Map<K,V> m) 方法将 HashMap 包装成一个线程安全的 Map。
·这种方式与 Hashtable 的锁机制类似(也是方法级同步),但提供了更好的灵活性。通常作为过渡方案,性能仍不如 ConcurrentHashMap。
最后,Hashtable 是一个过时的、不再被推荐使用的类。在新的代码中,你应该:
·需要非同步 Map -> 使用 HashMap ·需要同步 Map -> 使用 ConcurrentHashMap