Hashmap 和Hashtable的之间的差异

本文探讨了HashMap和Hashtable作为Java中实现Map接口的两种方式的主要区别,包括线程安全性、同步特性及性能差异。同时介绍了如何使HashMap具备线程安全特性,并推荐在多线程环境中使用ConcurrentHashMap。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

  1. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
  2. HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
  3. 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
  4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
  5. HashMap不能保证随着时间的推移Map中的元素次序是不变的。

要注意的一些重要术语:

1) sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。

2) Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

3) 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

我们能否让HashMap同步?

HashMap可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);

结论

Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

 

### 线程安全性 HashMap 是线程不安全的,这意味着在多线程环境下,如果多个线程同时修改了 HashMap 的结构(例如添加或删除元素),则可能会导致数据不一致的问题。为了在多线程环境中使用 HashMap,需要外部同步机制来保证线程安全。相比之下,Hashtable 是线程安全的,它的方法都是 synchronized 的,因此在多线程访问时不需要额外的同步措施[^2]。 ### 键值允许为 null 的情况 HashMap 允许键值都为 null,其中键只能有一个 null 值。而在 Hashtable 中,键值都不允许为 null,尝试插入 null 值会导致抛出 NullPointerException 异常。 ### 迭代器差异 HashMap 使用的是 Iterator,这是一种 fail-fast 迭代器,当迭代过程中集合被修改,除了通过迭代器自身的 remove 方法外,会抛出 ConcurrentModificationException 异常。而 Hashtable 使用的是 Enumeration 迭代器,它不是 fail-fast 的,因此在迭代过程中集合被修改不会抛出异常。 ### Hash 计算方式 HashMap 在计算 hash 值时进行了额外的处理,以减少哈希冲突的概率。而 Hashtable 直接使用了 key 的 hashCode 方法来计算 hash 值。 ### 默认初始大小与扩容方式 HashMap 默认初始大小为 16,并且容量必须是 2 的整数次幂,扩容时将容量变为原来的 2 倍。而 Hashtable 默认初始大小为 11,扩容时将容量变为原来的 2 倍加 1[^2]。 ### 是否包含 contains 方法 HashMap 没有 contains 方法,而 Hashtable 包含了类似于 containsValue 的 contains 方法。 ### 继承关系 HashMap 继承自 AbstractMap 类,而 Hashtable 继承自 Dictionary 类[^2]。 ### 数据结构 HashMap 采用数组、链表红黑树的数据结构,而非线程安全且无序,查找效率较高,初始化默认容量为 $2^4$,每次扩容为原来的 2 倍。而 Hashtable 采用数组链表的数据结构,线程安全,键值均不允许为 null,默认初始大小为 11,每次扩充为原来的 $2n+1$[^3]。 ### 同步机制 最大的不同在于 Hashtable 的方法是 synchronized 的,而 HashMap 不是,在多个线程访问 Hashtable 时,不需要自己为它的方法实现同步,而 HashMap 就必须提供外同步 (Collections.synchronizedMap)[^4]。 ```java // 示例代码:HashMap 需要外部同步 Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); ``` ### 相关问题 1. 如何在多线程环境中安全地使用 HashMap? 2. HashMap Hashtable 在性能上有何差异? 3. Java 中如何实现一个线程安全的 HashMap
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值