一、继承的父类不同
- HashMap继承自AbstractMap类,而Hashtable继承自Dictionary类。尽管它们实现的接口都是Map接口,但由于继承的父类不同,导致它们在一些行为和方法上存在差异。
二、线程安全性不同
- HashMap是非同步的,这意味着它不是线程安全的。在多个线程同时访问HashMap时,如果没有适当的同步机制,可能会导致数据不一致的问题。
- Hashtable是线程安全的,它的方法被synchronized修饰,因此在多线程环境下可以直接使用Hashtable而无需额外的同步处理。但需要注意的是,尽管Hashtable是线程安全的,但将Hashtable与多线程访问结合使用并不总是安全的,因为只有当方法是同步的时才能保证线程安全。
三、对null值的支持不同
- HashMap允许使用null作为键(key),但建议尽量避免这样做,因为当使用null作为键时,该键总是存储在table数组的第一个节点上,这可能会影响HashMap的性能。HashMap也允许一个或多个键所对应的值为null。
- Hashtable则不允许使用null作为键或值。如果尝试在Hashtable中插入null键或值,将在运行时抛出NullPointerException异常。
四、遍历方式的不同
- HashMap只支持Iterator遍历方式。
- Hashtable除了支持Iterator遍历方式外,还支持Enumeration遍历方式。这是由于Hashtable的历史原因造成的,它在Java早期版本中就已经存在,而那时还没有引入Iterator接口。
五、哈希计算方法与扩容机制不同
- HashMap在计算哈希值时,对key的hashCode进行了二次哈希,以获得更好的散列值,然后对table数组长度取模。当已用容量超过总容量乘以负载因子时,HashMap会进行扩容,扩容规则为当前容量翻倍(但不超过Integer.MAX_VALUE - 8)。
- Hashtable在计算哈希值时,直接使用key的hashCode对table数组的长度进行取模。当已用容量超过总容量乘以负载因子时,Hashtable也会进行扩容,但扩容规则为当前容量翻倍后加1(同样不超过允许的最大容量)。
六、其他区别
- HashMap没有提供contains(Object value)方法,而是提供了containsValue(Object value)和containsKey(Object key)方法来检查是否包含特定的值或键。
- Hashtable则保留了contains(Object value)方法(其功能与containsValue(Object value)相同),以及containsValue(Object value)和containsKey(Object key)方法。
- HashMap的迭代器(Iterator)是fail-fast的,这意味着如果在迭代过程中有其他线程修改了HashMap的结构(如增加或删除元素),将会抛出ConcurrentModificationException异常。但需要注意的是,这并不是一个一定会发生的行为,它取决于JVM的实现。而Hashtable的enumerator迭代器则不是fail-fast的。