HashMap和Hashtable的区别

本文详细比较了HashMap和Hashtable在多个方面的不同,包括线程安全性、NULL键值处理、类继承、初始化及扩容规则、迭代器特性以及性能差异。同时介绍了相关的重要概念,如synchronized机制、fail-fast机制等。

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

【1】HashMap和HashTable异同

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。

主要的区别有:

  • 线程安全性–同步(synchronization),
  • 键值是否允许为null,
  • 类继承,
  • 初始化及扩容
  • 迭代器以及速度。

① 线程安全

HashMap是非synchronized,而Hashtable是synchronized。Hashtable 所有的元素操作都是 synchronized 修饰的,而 HashMap 并没有。

这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable。而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

HashTable只能由一个线程操作, ConcurrentHashMap可以让一个线程操作第一个Segment,另一个线程操作另一个Segment

② NULL

Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null(只有一个键允许为null)。

HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键(key)和值(value),而Hashtable则不行)。

为什么 Hashtable 是不允许 KEY 和 VALUE 为 null, 而 HashMap 则可以?

Hashtable中如果key/ value 为 null 会直接抛出空指针异常,而 HashMap 的逻辑对 null 作了特殊处理。


③ 类继承

Hashtable如下:

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

HashMap如下:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

可以看出两者继承的类不一样,Hashtable 继承了 Dictionary类,而 HashMap 继承的是 AbstractMap 类。

Dictionary 是 JDK 1.0 添加的,很古老的一个字典类。其javadoc如下所示:

Dictionary是键值对映射类的抽象父类,例如Hashtable。

每一个键和值都是一个object。每个键最多关联一个值。

给定一个Dictionary对象和一个键,关联的元素可以被找到。

任何非 null 对象都可以用作键和值。

通常,这个类的实现应该使用equals方法来确定两个键是否相同

注意:这个类已经过时。新的实现应该实现Map接口,而不是继承这个类


④ 初始化和容量扩容

HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。

当现有容量大于总容量 * 负载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 + 1。


⑤ 迭代器

另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的Enumerator迭代器不是fail-fast的。

所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。

但这并不是一个一定发生的行为,要看JVM的实现。这同样也是Enumeration和Iterator的区别。

参考博文:浅谈从fail-fast机制到CopyOnWriteArrayList使用

⑥ 速度与性能

由于Hashtable是线程安全的也是synchronized,所以通常它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

如果要线程安全又要保证性能,建议使用 JUC 包下的 ConcurrentHashMap。


【2】相关重要术语

① sychronized意味着在一次仅有一个线程能够更改Hashtable。

就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。HashTable将底层整个hash表都锁了起来,故而在多线程的环境下,使用HashTable虽然安全但是性能十分低下。

另外需要注意的是HashTable并非绝对安全,比如在复合操作的情况下,如下所示:

if(!table.contains(XX)){
	//如果在这中间,被其他线程拿到了锁呢?
	table.put(XX);
}

② fail-fast机制

"快速失败"也就是fail-fast,它是Java集合的一种错误检测机制。

当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。

例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。

但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。

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

③ 我们能否让HashMap同步?

HashMap可以通过下面的语句进行同步:

Map m = Collections.synchronizeMap(hashMap);
// 或者直接使用ConcurrentHashMap(JDK1.5);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值