HashMap 与 ConcurrentHashMap 的区别

2025年3月27日 | 阅读 4 分钟

HashMap 是 Java 中一个强大的数据结构,用于存储键值对。它通过关联的键来映射值。它允许我们存储 null 值和 null 键。它是一个非同步的 Java 集合类。而 ConcurrentHashMap 作为 HashMap 的替代品被引入。 ConcurrentHashMap 是一个同步的集合类。

HashMap 是非线程安全的,不能在并发的多线程环境中使用。相比之下,ConcurrentHashMap 是线程安全的,专门为多线程和并发环境设计。

在本节中,我们将基于线程安全、同步、性能、用途等多个参数来比较 HashMap 和 ConcurrentHashMap 的区别。

以下是 HashMap 和 ConcurrentHashMap 的一些主要区别

  • 如上所述,HashMap 是非同步的、非线程安全的,而 ConcurrentHashMap 是同步的、线程安全的集合类。尽管 ConcurrentHashMap 的同步级别无法与 Hashtable 相比,但在大多数实际情况下,它的性能都很好。
  • HashMap 可以使用 Collection.syncronizedMap; 进行同步。它返回一个几乎与 Hashtable 相同的集合。
  • 同步后的 HashMap 的可扩展性不如 ConcurrentHashMap。
  • 在多线程环境中,ConcurrentHashMap 的性能优于同步 HashMap。
  • 在单线程环境中,HashMap 的性能略优于 ConcurrentHashMap。
  • 在 HashMap 中,如果一个线程正在迭代对象,而另一个线程想要修改对象,我们会得到一个 ConcurrentModificationException 运行时异常。但是在 ConcurrentHashMap 中,一个线程可以一边执行修改,一边允许另一个线程运行。
  • HashMap 引入于 Java 2 (JDK 1.2),ConcurrentHashMap 引入于 Java 5 (JDK 1.5)。

让我们看一些示例来理解 HashMap 和 ConcurrentHashMap 的行为。

HashMap 和 Concurrent HashMap 的示例

考虑以下示例来理解 HashMap 的行为。

示例 1

输出

{null=Sofia, 100=Stark, 101=Michale, 102=Ani}

现在使用 ConcuurentHashMap 来理解相同的示例。

使用 ConcurrentHashMap

输出

Exception in thread "main" java.lang.NullPointerException
	at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
	at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
	at ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:9)

从上面的示例可以看出,HashMap 允许键值对为空,但在 ConcurrentHashMap 中,我们不能将 null 值用于键值组合;否则会抛出 NullPointerException

示例 2

在 HashMap 中,如果一个线程正在迭代对象,而另一个线程尝试迭代对象,则会抛出运行时异常。但是,在 ConcurrentHashMap 中,可以同时由两个或多个线程迭代对象。

考虑下面的示例

使用 HashMap

// 使用 HashMap 通过两个线程同时添加对象的 Java 程序

输出

100=X
101=Y
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
	at java.util.HashMap$EntryIterator.next(HashMap.java:1479)
	at java.util.HashMap$EntryIterator.next(HashMap.java:1477)
	at HashMapExample2.main(HashMapExample2.java:27)

从上面的示例可以看出,当子线程尝试与其他线程同时添加对象时,会抛出运行时异常。让我们使用 ConcurrentHashMap 执行相同的代码。

使用 ConcurrentHashMap

输出

100=X
101=Y
102=Z
103=D
{100=X, 101=Y, 102=Z, 103=D}

从上面的示例可以看出,在 ConcurrentHashMap 中,我们可以使用两个不同的线程轻松地同时添加对象。但是,在 HashMap 中是不可能的。

ConcurrentHashMap 相对于 HashMap 的优点

使用 ConcurrentHashMap 的优点如下:

  • 在多线程环境中提供非常高的并发性。
  • 当写操作进行加锁时,读操作可以非常快。
  • 提供非对象级别锁定。
  • 使用多种锁。
  • 允许其他线程在某个线程迭代对象时进行迭代。
  • 在多线程场景下,尤其是多线程情况下,它是线程安全的。

让我们根据不同参数直接比较 HashMap 和 ConcurrentHashMap。

参数HashMapConcurrentHashMap
同步非同步synchronized
线程安全非线程安全线程安全
Iterator它是快速失败的,迭代时会抛出异常。它是安全失败的,支持多线程迭代。
空值允许存储 null 键和值。不允许存储 null 键/值。
性能更快比 HashMap 慢

总结

我们已经讨论了 HashMap 和 ConcurrentHashMap 之间所有主要的区别。为这次讨论做一个最终的总结,我们想说 ConcuurentHashMap 在多线程环境中很有益,并且比 HashMap 性能更好。它提供了线程安全、可伸缩性和同步。对于单线程环境,HashMap 的性能略优于 ConcurrentHashMap。