1. 简介

在本篇教程中,我们将深入探讨 Apache Commons Collections 库中提供的 MultiValuedMap 接口。

MultiValuedMap 提供了一种简洁的 API,用于在 Java 中将每个键映射到一个值集合。它是 org.apache.commons.collections4.MultiMap 的继任者,后者在 Commons Collections 4.1 中已被标记为废弃。

2. Maven 依赖

对于使用 Maven 的项目,我们需要添加如下依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.5.0-M2</version>
</dependency>

3. 向 MultiValuedMap 添加元素

我们可以通过 putputAll 方法来添加元素。

首先,创建一个 MultiValuedMap 实例:

MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();

接着,使用 put 方法逐个添加元素:

map.put("fruits", "apple");
map.put("fruits", "orange");

此外,我们还可以使用 putAll 方法一次性将多个值绑定到一个键上:

map.putAll("vehicles", Arrays.asList("car", "bike"));
assertThat((Collection<String>) map.get("vehicles"))
  .containsExactly("car", "bike");

4. 从 MultiValuedMap 中获取元素

MultiValuedMap 提供了多种方法用于获取键、值以及键值对。

4.1. 获取某个键的所有值

使用 get 方法可以获取某个键对应的所有值,返回的是一个 Collection

assertThat((Collection<String>) map.get("fruits"))
  .containsExactly("apple", "orange");

4.2. 获取所有键值对

使用 entries 方法可以获取所有键值对的集合:

Collection<Map.Entry<String, String>> entries = map.entries();

4.3. 获取所有键

有两种方式获取所有的键:

使用 keys 方法获取键的 MultiSet 视图:

MultiSet<String> keys = map.keys();
assertThat(keys).contains("fruits", "vehicles");

或者使用 keySet 方法获取键的 Set 视图:

Set<String> keys = map.keySet();
assertThat(keys).contains("fruits", "vehicles");

4.4. 获取所有值

使用 values 方法可以获取所有值组成的集合:

Collection<String> values = map.values();
assertThat(values).contains("apple", "orange", "car", "bike");

5. 从 MultiValuedMap 中删除元素

接下来我们看看如何从 MultiValuedMap 中删除元素。

5.1. 删除某个键的所有值

使用 remove 方法可以删除某个键对应的所有值,并返回这些值的集合:

Collection<String> removedValues = map.remove("fruits");
assertThat(map.containsKey("fruits")).isFalse();
assertThat(removedValues).contains("apple", "orange");

5.2. 删除某个键值对

如果只想删除某个键对应的某个特定值,可以使用 removeMapping 方法:

boolean isRemoved = map.removeMapping("fruits","apple");
assertThat(map.containsMapping("fruits","apple")).isFalse();

5.3. 清空所有映射

使用 clear 方法可以清空整个 MultiValuedMap

map.clear();
assertThat(map.isEmpty()).isTrue();

6. 检查 MultiValuedMap 中的元素

以下是常用的检查方法。

6.1. 判断某个键是否存在

使用 containsKey 方法:

assertThat(map.containsKey("vehicles")).isTrue();

6.2. 判断某个值是否存在

使用 containsValue 方法:

assertThat(map.containsValue("orange")).isTrue();

6.3. 判断某个键值对是否存在

使用 containsMapping 方法:

assertThat(map.containsMapping("fruits","orange")).isTrue();

6.4. 判断是否为空

使用 isEmpty 方法:

assertThat(map.isEmpty()).isFalse();

6.5. 获取映射数量

使用 size 方法获取所有值的总数:

assertEquals(4, map.size());

7. 常见实现类

Apache Commons Collections 提供了多种 MultiValuedMap 的实现。

7.1. ArrayListValuedHashMap

ArrayListValuedHashMap 内部使用 ArrayList 存储值,因此 允许重复键值对

MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
map.put("fruits", "apple");
map.put("fruits", "orange");
map.put("fruits", "orange");
assertThat((Collection<String>) map.get("fruits"))
  .containsExactly("apple", "orange", "orange");

⚠️ 注意:该类不是线程安全的,多线程环境下需要手动同步。

7.2. HashSetValuedHashMap

HashSetValuedHashMap 使用 HashSet 存储值,因此 不允许重复键值对

MultiValuedMap<String, String> map = new HashSetValuedHashMap<>();
map.put("fruits", "apple");
map.put("fruits", "apple");
assertThat((Collection<String>) map.get("fruits"))
  .containsExactly("apple");

可以看到,重复的值被自动去重。

⚠️ 同样地,该类也不是线程安全的

7.3. UnmodifiableMultiValuedMap

UnmodifiableMultiValuedMap 是一个装饰器类,用于创建不可变的 MultiValuedMap 实例:

@Test(expected = UnsupportedOperationException.class)
public void givenUnmodifiableMultiValuedMap_whenInserting_thenThrowingException() {
    MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
    map.put("fruits", "apple");
    map.put("fruits", "orange");
    MultiValuedMap<String, String> immutableMap =
      MultiMapUtils.unmodifiableMultiValuedMap(map);
    immutableMap.put("fruits", "banana"); // throws exception
}

❌ 任何修改操作都会抛出 UnsupportedOperationException

8. 总结

我们介绍了 Apache Commons Collections 中 MultiValuedMap 接口的各种常用方法,并演示了几种常见的实现类。在实际开发中,如果需要一个键映射多个值的场景,MultiValuedMap 是一个简单粗暴又实用的选择。


原始标题:Guide to Apache Commons MultiValuedMap | Baeldung