1. 概述

在这篇文章中,我们将探讨Google Guava库中的Multimap实现。它是一种类似于java.util.Map的集合,但每个键可以关联多个值。

2. Maven依赖

首先,我们添加一个依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

最新版本可以在这里找到。

3. Multimap实现

对于Guava的Multimap,如果为同一个键添加两个值,第二个值不会覆盖第一个值,而是会在结果map中保留两个值。让我们看一个测试用例:

String key = "a-key";
Multimap<String, String> map = ArrayListMultimap.create();

map.put(key, "firstValue");
map.put(key, "secondValue");

assertEquals(2, map.size());

打印map的内容将输出:

{a-key=[firstValue, secondValue]}

当我们通过键“a-key”获取值时,我们会得到一个包含“firstValue”和“secondValue”的Collection<String>

Collection<String> values = map.get(key);

打印值将输出:

[firstValue, secondValue]

4. 与标准Map比较

java.util包中的标准Map不允许我们将多个值分配给相同的键。考虑一个简单的案例,我们使用相同的键向Map中添加两个值:

String key = "a-key";
Map<String, String> map = new LinkedHashMap<>();

map.put(key, "firstValue");
map.put(key, "secondValue");

assertEquals(1, map.size());

结果的map只有一个元素(“secondValue”),因为第二次put()操作覆盖了第一个值。如果我们想在Guava的Multimap中实现相同的行为,我们需要创建一个Map,其值类型为List<String>

String key = "a-key";
Map<String, List<String>> map = new LinkedHashMap<>();

List<String> values = map.get(key);
if(values == null) {
    values = new LinkedList<>();
    values.add("firstValue");
    values.add("secondValue");
 }

map.put(key, values);

assertEquals(1, map.size());

显然,这不是很方便。如果我们的代码中需要这种功能,那么Guava的Multimap可能比java.util.Map更合适。

这里需要注意的是,尽管列表中有两个元素,但size()方法返回1。在Multimap中,size()返回存储在Map中的实际值的数量,而keySet().size()返回唯一键的数量。

5. Multimap的优点

Multimap通常用于需要Map<K, Collection<V>>的地方。区别包括:

  • 在添加条目之前不需要先填充空集合,使用put()即可
  • get()方法永远不会返回null,只会返回一个空集合(无需像Map<String, Collection<V>>的测试用例那样检查null)
  • 如果一个键映射至少有一个值,则该键存在于Multimap中。任何导致某个键关联值为零的操作都会删除该键(在Map<String, Collection<V>>中,即使我们从集合中移除所有值,我们仍保留一个空的Collection作为值,这是不必要的内存开销)
  • 可以通过size()获取总条目值的数量

6. 结论

本文展示了如何以及何时使用Guava的Multimap。它将Multimap与标准的java.util.Map进行了比较,并突出了GuavaMultimap的优势。

所有这些示例和代码片段可以在GitHub项目中找到——这是一个Maven项目,可以直接导入并运行,因为它已经配置好。