HashMap 和 Hashtable 的区别
1. 父类不同
- HashMap:继承自
AbstractMap类。 - Hashtable:继承自
Dictionary类。
两者都实现了 Map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
2. 对外提供的接口不同
- Hashtable:比
HashMap多提供了elements()和contains()两个方法。elements()方法继承自Dictionary类,用于返回此Hashtable中的value的枚举。contains()方法判断该Hashtable是否包含传入的value,其作用与containsValue()一致。实际上,containsValue()只是调用了contains()方法。
3. 对 null 的支持不同
- Hashtable:
key和value都不能为null。 - HashMap:
key可以为null,但只能有一个这样的key,因为必须保证key的唯一性;可以有多个key值对应的value为null。
4. 线程安全性不同
- HashMap:线程不安全,在多线程并发的环境下,可能会产生问题,如死锁等。需要开发人员自行处理多线程的安全问题。
- Hashtable:线程安全,每个方法上都有
synchronized关键字,因此可以直接用于多线程环境中。
尽管 HashMap 是线程不安全的,但其效率远高于 Hashtable。这是因为大部分使用场景都是单线程的。当需要多线程操作时,可以使用线程安全的 ConcurrentHashMap。ConcurrentHashMap 的效率比 Hashtable 高很多,因为它使用了分段锁,并不对整个数据进行锁定。
5. 初始容量和扩充容量大小不同
- HashMap:默认初始容量为 16,每次扩充容量为原来的两倍。
- Hashtable:默认初始容量为 11,每次扩充容量为原来的两倍加一。
6. 计算 hash 值的方法不同
-
HashMap:使用自身的 hash 算法,默认采用对象的
hashCode值,并对其进行再加工(扰动函数),以减少冲突。static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } -
Hashtable:直接使用对象的
hashCode值,计算方式较简单。int hash = key.hashCode();
总结
| 特性 | HashMap | Hashtable |
|---|---|---|
| 父类 | AbstractMap | Dictionary |
| 额外方法 | 无 | elements(), contains() |
| 对 null 的支持 | key 和 value 可以为 null | key 和 value 不能为 null |
| 线程安全性 | 线程不安全 | 线程安全 |
| 初始容量和扩充容量 | 初始容量 16,扩充容量为原来的两倍 | 初始容量 11,扩充容量为原来的两倍加一 |
| hash 值计算 | 使用自定义的 hash 算法 | 使用对象的 hashCode 值 |
选择使用哪种数据结构应根据具体的使用场景来决定。如果在多线程环境下,需要使用线程安全的集合,可以选择 ConcurrentHashMap,而不是 Hashtable。