HashMap、ConcurrentHashMap、Hashtable

213 阅读3分钟

当详细展开关于HashMapConcurrentHashMapHashtable的比较时,我们可以从底层原理、特点、线程安全性、是否允许null值、实现逻辑、扩容机制、遍历方式和使用场景等多个维度进行探讨。

1. 底层原理

  • HashMap:基于哈希表的Map接口实现,使用链表数组来存储键值对。在JDK 1.8及以后版本中,当链表长度超过8并且数组长度大于等于64时,链表会转换为红黑树以提高查询效率。
  • ConcurrentHashMap:同样是基于哈希表的Map接口实现,但它在JDK 1.7中使用了分段锁技术,将数据分为多个段(Segment),每个段独立加锁以提高并发性能。在JDK 1.8及以后版本中,取消了分段锁,采用了基于节点的锁(如CAS操作和synchronized锁)来管理节点的添加、删除等操作,进一步提高了并发性能。
  • Hashtable:也是基于哈希表的Map接口实现,但与HashMap和ConcurrentHashMap不同,它在所有非空方法上都添加了synchronized关键字,对整个表进行加锁,以保证线程安全。

2. 特点

  • HashMap
    • 允许使用null值和null键。
    • 不保证映射的顺序,特别是它不保证该顺序随时间的推移保持不变。
    • 非线程安全,需要外部同步来保障多线程环境下的线程安全。
  • ConcurrentHashMap
    • 线程安全,无需外部同步。
    • 提供了比Hashtable更高的并发性能。
    • 允许使用null值和null键(但不建议使用null键,因为null不能作为同步锁)。
  • Hashtable
    • 线程安全,所有非空方法都是同步的。
    • 不允许使用null值和null键。
    • 不保证映射的顺序。

3. 线程安全性

  • HashMap:非线程安全。
  • ConcurrentHashMap:线程安全,通过内部锁机制保证。
  • Hashtable:线程安全,通过方法上的synchronized关键字保证。

4. 是否允许null值

  • HashMap:允许null值和null键。
  • ConcurrentHashMap:允许null值和null键(但不建议使用null键)。
  • Hashtable:不允许null值和null键。

5. 实现逻辑

  • HashMapConcurrentHashMap在插入、删除和查找等操作时,都会先计算键的哈希码,然后定位到数组中的某个位置。如果该位置为空,则直接插入;如果已有元素,则根据链表或红黑树(在HashMap中)或节点的锁(在ConcurrentHashMap中)来进行处理。
  • Hashtable在实现逻辑上与HashMap类似,但由于它在所有非空方法上都添加了synchronized关键字,因此在进行任何操作时都需要先获取锁。

6. 扩容机制

  • HashMapHashtable在扩容时都会创建一个新的容量更大的数组,并将旧数组中的数据重新计算哈希值后插入到新数组中。但Hashtable在扩容时也会进行同步操作。
  • ConcurrentHashMap在JDK 1.8及以后版本中,采用了更加复杂的扩容机制,包括使用CAS操作来更新数组大小和迁移元素等。

7. 遍历方式

  • HashMapConcurrentHashMapHashtable都提供了迭代器(Iterator)和分割器(Spliterator)来遍历Map中的元素。此外,它们还提供了keySet()、values()和entrySet()等方法来返回包含所有键、值或键值对的集合,这些集合也支持遍历。
  • 需要注意的是,ConcurrentHashMap的迭代器是弱一致性的,这意味着在迭代器创建之后,对ConcurrentHashMap的修改可能不会反映在迭代器中。

8. 使用场景

  • HashMap:适用于单线程环境下的键值对存储,或者并发量不高且可以通过外部同步来保障线程安全的多线程环境。
  • ConcurrentHashMap:适用于需要高并发访问的键值对存储场景,特别是在多核处理器环境中。
  • Hashtable:虽然在Java早期版本中常用作线程安全的Map实现,但在现代Java开发中,更推荐使用ConcurrentHashMap来替代Hashtable,因为ConcurrentHashMap提供了更高的并发性能和更好的灵活性。