相同点:
- 均实现了Map、Cloneable、Serializable三个接口,均可以被序列化或克隆。
- 负载因子默认均为 0.75
- 二者的存储结构相同,解决冲突的方法略有不同
不同点:
-
实现方式不同
-
HashMap 继承自 AbstractMap 抽象类
-
Hashtable 继承自 Directory 抽象类
-
jdk 1.2 开始,
Map
接口被引入,它提供了更好的键值对映射实现,Dictionary
抽象类也被废弃了。 -
Dictionary
类只允许键和值的类型为Object
,而Map
接口支持泛型,可以指定键和值的类型。 -
Dictionary
类的方法命名与Map
接口也有所不同,Dictionary
类中的方法命名较为复杂,而Map
接口中的方法命名更加简洁明了。
-
-
-
线程安全方面
HashMap
是非线程安全的,只适用于单线程环境下,多线程环境下可以采用concurrent
并发包下的ConcurrentHashMap
。Hashtable
是线程安全的,能用于多线程环境中。
ConcurrentHashMap
和Hashtable
都是线程安全的。在早期版本中,Hashtable
比较常用,但是在目前的 Java 版本中,ConcurrentHashMap
更为常用,原因如下:-
同步机制差异
Hashtable
使用了 synchronized 关键字来保证线程安全,即在每个公共方法上都进行了同步锁定。而ConcurrentHashMap
则使用了分离锁技术来实现并发访问,它将哈希表分成多个段(Segment),每个段都拥有自己的锁,不同的线程可以同时访问不同的段,从而提高了并发性能。 -
迭代器支持不同
由于ConcurrentHashMap
使用了分离锁技术,因此不能保证在迭代时对所有的元素都能获取到正确的结果,可能会遗漏一些元素。而Hashtable
则没有这个问题,遍历时可以保证获取到所有的元素。由于在遍历时不会对整个哈希表进行加锁,因此在遍历期间,有可能会有其他线程对哈希表进行修改,导致遍历时漏掉一些元素或者重复访问一些元素,从而导致遍历结果不准确。为了解决这个问题,ConcurrentHashMap提供了一些迭代器的变种,如KeySetView、EntrySetView和Values等,可以保证遍历时不会遗漏任何元素。
-
同步性能差异
Hashtable
中的所有方法都是同步的,而ConcurrentHashMap
仅在需要同步的时候进行同步,所以ConcurrentHashMap
的性能更高。 -
功能差异
ConcurrentHashMap
比Hashtable
更加丰富,例如支持keySet()
、values()
和entrySet()
等方法。 -
可扩展性差异
Hashtable
不支持扩展(自动扩容),而ConcurrentHashMap
可以根据需要自动扩展。
-
数据结构方面
-
jdk1.8 开始,HashMap加入了红黑树
-
Hashtable则一直为链表
-
-
Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null。
-
hash值算法不同
-
HashMap添加元素时,使用hash()方法,增加hashCode的随机性
-
Hashtable添加元素时,是直接采用key的hashCode()
-
-
索引算法不同
-
Hashtable 在计算索引时,用 % 运算
-
HashMap 在计算索引时,则用 & 运算(数组长度为2的幂次方,而且位运算更高效)
-
-
初始化容量不同
-
HashMap 的初始容量为:16,要求数组容量一定为2的整数次幂
-
Hashtable 初始容量为:11,不要求数组容量一定为2的整数次幂
两者的负载因子默认都是:0.75。
-
-
扩容机制不同
当 已用容量 > 总容量 * 负载因子 时
- HashMap 扩容规则为当前容量翻倍
- Hashtable 扩容规则为当前容量翻倍 +1
-
迭代器不同
-
HashMap 使用 Iterator 迭代器(支持 fail-fast 机制)
fail-fast 机制
-
如果在迭代过程中,被遍历的集合被修改了(比如添加或删除元素),就会抛出一个ConcurrentModificationException异常,从而提供一种迭代器与集合同步的机制。
-
这种机制是通过迭代器(Iterator)来实现的,迭代器中维护了一个操作计数器(modCount),每当集合结构发生改变时,计数器的值会增加。在迭代器遍历集合时,如果发现计数器的值与开始遍历时不一致,就会抛出ConcurrentModificationException异常,以保证集合的一致性和避免并发修改问题。
-
如果使用
Iterator.remove()
方法删除元素时,该方法本身会更新modCount
的值,因此不会出现计数器不一致的情况(单线程环境下,若多线程环境,则可能抛出ConcurrentModificationException异常)。
-
-
Hashtable 支持 Iterator 和 Enumeration 两种
enumerator
迭代器是一个旧的Java集合框架中的迭代器接口,已经被新的Iterator
接口所取代。在JDK 1.0和1.1中,Java集合框架只提供了enumerator
接口来进行集合的迭代操作。在Java 2中,新增了Iterator
接口来替换enumerator
接口,Iterator
接口提供了更强大和灵活的迭代功能,并且支持 fail-fast 机制。
-
-
部分API不同
-
HashMap不支持contains(Object value)方法,没有重写toString()方法
-
HashTable支持contains(Object value)方法,而且重写了toString()方法
-