> 技术文档 > Java Multimap :实现类与操作示例

Java Multimap :实现类与操作示例


一、Multimap 概述

Multimap 是 Google Guava 库中提供的一种集合类型,它扩展了传统的 Map 概念,允许一个键对应多个值。与标准的 Map<K, List>Map<K, Set> 相比,Multimap 提供了更简洁的 API 和更强大的功能。

Multimap 主要特点:

  • 一个键可以映射到多个值
  • 避免了手动管理值集合的麻烦
  • 提供了丰富的视图集合
  • 支持不可变实现

二、Multimap 实现类

Guava 提供了多种 Multimap 实现,每种实现都有不同的特性和使用场景。

1. ListMultimap 系列

特点:值以 List 形式存储,允许重复值,保留插入顺序

实现类 描述 是否线程安全 ArrayListMultimap 使用 ArrayList 作为值集合 否 LinkedListMultimap 使用 LinkedList 作为值集合 否 ImmutableListMultimap 不可变实现 是

2. SetMultimap 系列

特点:值以 Set 形式存储,不允许重复值

实现类 描述 是否线程安全 HashMultimap 使用 HashSet 作为值集合 否 LinkedHashMultimap 使用 LinkedHashSet 作为值集合,保留插入顺序 否 TreeMultimap 使用 TreeSet 作为值集合,按键和值排序 否 ImmutableSetMultimap 不可变实现 是

3. 其他实现

实现类 描述 是否线程安全 Multimaps.synchronizedMultimap 同步包装器,使任何 Multimap 线程安全 是 Multimaps.unmodifiableMultimap 不可修改视图 是

三、Multimap 基本操作示例

1. 创建 Multimap

// 创建ArrayListMultimapListMultimap<String, String> listMultimap = ArrayListMultimap.create();// 创建HashMultimapSetMultimap<String, Integer> setMultimap = HashMultimap.create();// 创建不可变MultimapImmutableListMultimap<String, String> immutableMultimap = ImmutableListMultimap.of( \"key1\", \"value1\", \"key1\", \"value2\", \"key2\", \"value3\");

2. 添加元素

ListMultimap<String, String> multimap = ArrayListMultimap.create();// 添加单个元素multimap.put(\"fruit\", \"apple\");multimap.put(\"fruit\", \"banana\");multimap.put(\"fruit\", \"orange\");multimap.put(\"vegetable\", \"carrot\");// 添加多个元素multimap.putAll(\"fruit\", Arrays.asList(\"pear\", \"grape\"));multimap.putAll(\"vegetable\", Arrays.asList(\"potato\", \"tomato\"));

3. 获取元素

// 获取某个键的所有值List<String> fruits = multimap.get(\"fruit\"); // [apple, banana, orange, pear, grape]// 获取第一个值String firstFruit = multimap.get(\"fruit\").get(0); // apple// 检查键是否存在boolean hasFruit = multimap.containsKey(\"fruit\"); // true// 检查键值对是否存在boolean hasApple = multimap.containsEntry(\"fruit\", \"apple\"); // true

4. 删除元素

// 删除键的所有值multimap.removeAll(\"fruit\"); // 返回被删除的值列表// 删除特定键值对multimap.remove(\"vegetable\", \"tomato\"); // 返回boolean表示是否删除成功// 清空所有元素multimap.clear();

5. 视图操作

// 获取所有键的集合(去重)Set<String> keys = multimap.keySet();// 获取所有值的集合(不去重)Collection<String> values = multimap.values();// 获取键值对集合Collection<Map.Entry<String, String>> entries = multimap.entries();// 将Multimap转换为Map<K, Collection>Map<String, Collection<String>> mapView = multimap.asMap();

四、不同实现类的具体示例

1. ArrayListMultimap 示例

// 创建ArrayListMultimapListMultimap<String, Integer> scores = ArrayListMultimap.create();// 添加元素scores.put(\"Alice\", 90);scores.put(\"Alice\", 85);scores.put(\"Bob\", 75);scores.put(\"Bob\", 80);scores.put(\"Bob\", 82);// 获取元素List<Integer> aliceScores = scores.get(\"Alice\"); // [90, 85]List<Integer> bobScores = scores.get(\"Bob\"); // [75, 80, 82]// 允许重复值scores.put(\"Alice\", 90);List<Integer> newAliceScores = scores.get(\"Alice\"); // [90, 85, 90]

2. HashMultimap 示例

// 创建HashMultimapSetMultimap<String, String> tags = HashMultimap.create();// 添加元素tags.put(\"article1\", \"tech\");tags.put(\"article1\", \"java\");tags.put(\"article1\", \"programming\");tags.put(\"article2\", \"design\");tags.put(\"article2\", \"ui\");// 尝试添加重复值tags.put(\"article1\", \"java\"); // 不会有任何效果// 获取元素Set<String> article1Tags = tags.get(\"article1\"); // [tech, java, programming]Set<String> article2Tags = tags.get(\"article2\"); // [design, ui]

3. TreeMultimap 示例

// 创建TreeMultimap(按键和值排序)TreeMultimap<String, Integer> sortedScores = TreeMultimap.create();// 添加元素(乱序)sortedScores.put(\"Bob\", 80);sortedScores.put(\"Alice\", 90);sortedScores.put(\"Bob\", 75);sortedScores.put(\"Alice\", 85);// 获取元素(自动排序)SortedSet<Integer> aliceSortedScores = sortedScores.get(\"Alice\"); // [85, 90]SortedSet<Integer> bobSortedScores = sortedScores.get(\"Bob\"); // [75, 80]// 整个Multimap也是按键排序的System.out.println(sortedScores); // {Alice=[85, 90], Bob=[75, 80]}

4. ImmutableListMultimap 示例

// 创建不可变MultimapImmutableListMultimap<String, String> immutableMap = ImmutableListMultimap.<String, String>builder() .put(\"colors\", \"red\") .put(\"colors\", \"green\") .put(\"colors\", \"blue\") .put(\"shapes\", \"circle\") .put(\"shapes\", \"square\") .build();// 尝试修改会抛出UnsupportedOperationException// immutableMap.put(\"colors\", \"yellow\"); // 错误!// 安全地获取数据List<String> colors = immutableMap.get(\"colors\"); // [red, green, blue]

五、高级操作与工具方法

1. 使用 Multimaps 工具类

// 从Map<K, Collection>创建MultimapMap<String, List<Integer>> map = new HashMap<>();map.put(\"a\", Arrays.asList(1, 2, 3));map.put(\"b\", Arrays.asList(4, 5));ListMultimap<String, Integer> multimap = Multimaps.forMap(map);// 转换Multimap的值类型ListMultimap<String, String> stringMultimap = ArrayListMultimap.create();stringMultimap.put(\"key\", \"1\");stringMultimap.put(\"key\", \"2\");ListMultimap<String, Integer> intMultimap = Multimaps.transformValues(stringMultimap, Integer::valueOf);// 反转Multimap(键值互换)SetMultimap<Integer, String> inverted = Multimaps.invertFrom(stringMultimap, HashMultimap.create());

2. 过滤操作

ListMultimap<String, Integer> scores = ArrayListMultimap.create();scores.put(\"Alice\", 90);scores.put(\"Alice\", 85);scores.put(\"Bob\", 75);scores.put(\"Bob\", 80);// 过滤出分数大于80的条目Multimap<String, Integer> highScores = Multimaps.filterEntries(scores, entry -> entry.getValue() > 80);System.out.println(highScores); // {Alice=[90, 85]}

3. 同步包装

ListMultimap<String, String> unsafeMultimap = ArrayListMultimap.create();// 创建线程安全版本ListMultimap<String, String> safeMultimap = Multimaps.synchronizedListMultimap(unsafeMultimap);// 现在可以安全地在多线程环境中使用safeMultimap.put(\"key\", \"value\");

六、性能比较与选择指南

实现类 键存储 值存储 允许重复值 顺序保证 典型用途 ArrayListMultimap HashMap ArrayList 是 插入顺序 需要保留插入顺序且允许重复值的场景 LinkedListMultimap LinkedHashMap LinkedList 是 插入顺序 需要频繁在中间插入/删除的场景 HashMultimap HashMap HashSet 否 无 需要快速查找且不需要重复值的场景 LinkedHashMultimap LinkedHashMap LinkedHashSet 否 插入顺序 需要保留插入顺序且不允许重复值的场景 TreeMultimap TreeMap TreeSet 否 排序顺序 需要按键和值排序的场景 ImmutableListMultimap 不可变 不可变 是 构造顺序 需要不可变集合的场景 ImmutableSetMultimap 不可变 不可变 否 构造顺序 需要不可变集合且不允许重复值的场景

选择建议

  1. 如果需要允许重复值 - 选择 ListMultimap 实现
  2. 如果需要快速查找且不允许重复值 - 选择 SetMultimap 实现
  3. 如果需要排序功能 - 选择 TreeMultimap
  4. 如果需要线程安全 - 使用不可变实现或同步包装器
  5. 如果数据不常变化 - 优先考虑不可变实现

七、常见问题解答

Q1: Multimap 和 Map 有什么区别?

A1: 主要区别在于:

  • Multimap 提供了更简洁的 API,不需要手动管理值集合
  • Multimap 提供了丰富的视图方法(如 entries(), keys(), values())
  • Multimap 隐藏了实现细节,可以更灵活地切换底层实现
  • Multimap 的方法更直观,如 put() 直接添加元素而不需要先检查是否存在集合

Q2: 如何将 Multimap 转换为传统的 Map?

A2: 可以使用 asMap() 方法:

ListMultimap<String, String> multimap = ArrayListMultimap.create();multimap.put(\"key\", \"value1\");multimap.put(\"key\", \"value2\");Map<String, Collection<String>> map = multimap.asMap();

Q3: Multimap 是线程安全的吗?

A3: 大多数 Multimap 实现不是线程安全的,除了:

  • 不可变实现(ImmutableListMultimap, ImmutableSetMultimap)
  • 使用 Multimaps.synchronizedMultimap() 包装的 Multimap

Q4: 如何统计 Multimap 中每个键对应的值数量?

A4: 可以使用 Multimap 的 keys() 方法结合 Multisets:

ListMultimap<String, String> multimap = ArrayListMultimap.create();// 添加元素...Multiset<String> counts = HashMultiset.create(multimap.keys());System.out.println(counts); // 显示每个键的出现次数

八、总结

Guava 的 Multimap 提供了一种优雅的方式来处理键到多个值的映射关系,比传统的 Map<K, Collection> 更加方便和强大。通过选择合适的实现类,可以满足各种不同的业务需求,包括是否需要允许重复值、是否需要保持顺序、是否需要排序等功能。

在实际开发中,Multimap 特别适用于以下场景:

  • 标签系统(一个项目有多个标签)
  • 学生成绩记录(一个学生有多门成绩)
  • 反向索引(一个单词出现在多个文档中)
  • 分组统计(按类别分组记录)

掌握 Multimap 的使用可以显著简化代码,提高开发效率,是 Java 开发者工具箱中不可或缺的工具之一。

十堰房产