提升编程效率的API利器: 精通Google Guava库之多值Multimap

文摘   2024-10-29 19:29   广东  

一、Multimap 的核心特点

二、Multimap常用方法

三、常用的 Multimap 实现

    • 1. ArrayListMultimap

    • 2. HashMultimap

    • 3. LinkedHashMultimap

    • 4. TreeMultimap

    • 5.ImmutableSetMultimap和 ImmutableListMultimap

    • 6.SynchronizedMultimap

    • 7.ForwardingMultimap

一、Multimap 的核心特点

Multimap 最核心的特点就是支持一个键对应多个值。这意味着我们可以向 Multimap 中添加一个键和多个值,并且可以通过键来检索到对应的值集合。这种一对多的映射关系在很多场景下都非常有用,比如处理用户的多个邮箱地址、一个订单包含多个商品等。

除了支持多值映射外,Multimap 还具有以下特点:

  • 「值集合不必唯一:」 与 SetMultimap 不同,普通的 Multimap 允许值重复。如果你需要值集合中的元素唯一,可以选择使用 SetMultimap。

  • 「顺序可保留也可不保留:」 Guava 提供了多种 Multimap 的实现,其中一些实现可以保留元素插入的顺序,如 LinkedHashMultimap,而另一些实现则不保证顺序,如 HashMultimap。你可以根据具体需求选择合适的实现。

  • 「空键和空值的支持:」 Multimap 允许使用 null 作为键或值,但是不同的实现可能会有不同的限制。在选择具体的 Multimap 实现时,需要注意其对空键和空值的处理方式。

  • 「丰富的视图:」 Multimap 提供了多种视图来访问和操作其中的元素。通过 asMap() 方法,你可以获取一个将键映射到对应值集合的 Map 视图;通过 entries() 方法,你可以获取一个包含所有键值对集合的视图。这些视图提供了方便的方式来遍历和操作 Multimap 中的元素。

二、Multimap常用方法

直接上代码吧,Guava库中Multimap常用方法:

<dependency>      <groupId>com.google.guava</groupId>      <artifactId>guava</artifactId>      <version>****</version> </dependency>
import com.google.common.collect.ArrayListMultimap;  import com.google.common.collect.Multimap;    import java.util.Collection;  import java.util.Map;    public class MultimapExample {        public static void main(String[] args) {          // 创建一个Multimap实例          Multimap<String, Integer> multimap = ArrayListMultimap.create();            // 使用put方法添加键值对          multimap.put("apple", 1);          multimap.put("apple", 2);          multimap.put("banana", 3);          multimap.put("orange", 4);          multimap.put("orange", 5);          multimap.put("orange", 6);            // 使用putAll方法添加多个键值对          Map<String, Integer> moreFruits = Map.of("grape", 7, "grape", 8, "kiwi", 9);          multimap.putAll(moreFruits);            // 使用get方法获取键对应的所有值          System.out.println("Apples: " + multimap.get("apple")); // 输出 [1, 2]            // 使用containsKey和containsValue检查键和值是否存在          System.out.println("Contains key 'apple'? " + multimap.containsKey("apple")); // 输出 true          System.out.println("Contains value 6? " + multimap.containsValue(6)); // 输出 true            // 使用remove方法移除单个键值对          boolean removed = multimap.remove("banana", 3);          System.out.println("Removed banana=3? " + removed); // 输出 true            // 使用removeAll方法移除一个键及其所有值          multimap.removeAll("kiwi");          System.out.println("Kiwis: " + multimap.get("kiwi")); // 输出 []            // 使用keySet获取所有不同的键          System.out.println("Unique keys: " + multimap.keySet()); // 输出可能包含 [orange, apple, grape, banana] (注意banana和kiwi可能已被移除)            // 使用values获取所有值(包括重复值)          System.out.println("All values: " + multimap.values()); // 输出包含所有值的集合            // 使用entries获取所有键值对          System.out.println("Entries: " + multimap.entries()); // 输出所有键值对            // 使用asMap获取每个键对应值的集合的视图          System.out.println("As Map: " + multimap.asMap()); // 输出类似 {orange=[4, 5, 6], apple=[1, 2], grape=[7, 8]}            // 使用size方法获取键值对总数          System.out.println("Size of multimap: " + multimap.size()); // 输出当前multimap中键值对的总数            // 使用isEmpty检查multimap是否为空          System.out.println("Is multimap empty? " + multimap.isEmpty()); // 根据multimap内容输出 true 或 false      }  }

根据具体需求选择使用Multimap的哪种实现(ArrayListMultimap、HashMultimap、LinkedListMultimap等)。

三、常用的 Multimap 实现

Guava 提供了多种 Multimap 的实现,每种实现都有其特定的用途和性能特点:

1. ArrayListMultimap

如果需要保留值的插入顺序,并且希望获得较好的迭代性能,那么 ArrayListMultimap 是一个不错的选择。它基于 ArrayList 实现,提供了常数时间的 get 操作。但是需要注意的是,在某些情况下,如遍历所有键值对时,性能可能不如其他实现。

2. HashMultimap

如果对值的顺序不关心,但是需要快速的键查找性能,那么 HashMultimap 是一个很好的选择。它基于 HashMap 实现,提供了高效的键查找操作。但是需要注意的是,HashMultimap 不允许 null 键或值。

3. LinkedHashMultimap

如果既需要快速的键查找性能,又希望保留值的插入顺序,那么 LinkedHashMultimap 是一个很好的折中选择。它结合了 HashMap 和 LinkedList 的特点,既提供了快速的键查找性能,又保留了元素插入的顺序。

4. TreeMultimap

如果需要按键的顺序访问 Multimap 中的元素,并且希望根据键进行排序,那么 TreeMultimap 是一个很好的选择。它基于 TreeMap 实现,可以根据键的自然顺序或提供的 Comparator 对键进行排序。

模拟简单的社交场景,其中有用户、他们的朋友以及他们之间的互动

import com.google.common.collect.*;    import java.util.*;    public class SocialNetwork {        public static void main(String[] args) {          // 使用ArrayListMultimap来保存用户的朋友列表,保留插入顺序          ArrayListMultimap<String, String> friendsByUser = ArrayListMultimap.create();                    // 使用HashMultimap来保存用户对朋友的点赞,不保证顺序          HashMultimap<String, String> likesByUser = HashMultimap.create();                    // 使用LinkedHashMultimap来保存用户的消息,保留插入顺序          LinkedHashMultimap<String, String> messagesByUser = LinkedHashMultimap.create();                    // 使用TreeMultimap来保存用户的活动日志,按键(时间戳)排序          TreeMultimap<Long, String> activityLog = TreeMultimap.create();            // 添加一些朋友          friendsByUser.putAll("Alice", Arrays.asList("Bob", "Charlie", "David"));          friendsByUser.putAll("Bob", Arrays.asList("Alice", "Charlie"));          friendsByUser.put("Charlie", "David");            // 添加一些点赞          likesByUser.put("Alice", "Bob's post");          likesByUser.put("Alice", "Charlie's photo");          likesByUser.put("Bob", "Charlie's photo");          likesByUser.putAll("Charlie", Arrays.asList("Alice's status", "David's video"));            // 添加一些消息          messagesByUser.put("Alice", "Hi Bob!");          messagesByUser.put("Alice", "How are you Charlie?");          messagesByUser.put("Bob", "Charlie, where are you?");          messagesByUser.putAll("Charlie", Arrays.asList("Here I am!", "Alice, check this out!"));            // 添加一些活动日志(假设时间戳是简单的顺序数字)          activityLog.put(1L, "Alice added a new friend");          activityLog.put(2L, "Bob liked a photo");          activityLog.put(3L, "Charlie sent a message");            // 打印出Alice的朋友列表          System.out.println("Alice's friends: " + friendsByUser.get("Alice"));            // 打印出点赞了Charlie的照片的用户          for (String user : likesByUser.keys()) {              if (likesByUser.containsEntry(user, "Charlie's photo")) {                  System.out.println(user + " liked Charlie's photo");              }          }            // 打印出Alice发送的所有消息          for (String message : messagesByUser.get("Alice")) {              System.out.println("Alice said: " + message);          }            // 打印出按时间顺序的所有活动日志          for (Map.Entry<Long, String> entry : activityLog.entries()) {              System.out.println("Timestamp: " + entry.getKey() + ", Event: " + entry.getValue());          }            // 假设我们想要移除Bob作为Alice的朋友          friendsByUser.remove("Alice", "Bob");            // 再次打印出Alice的朋友列表,确认Bob已被移除          System.out.println("Alice's friends after removing Bob: " + friendsByUser.get("Alice"));      }  }

5. ImmutableSetMultimap 和 ImmutableListMultimap

如果你需要一个不可变的 Multimap,即一旦创建就不能修改,那么可以选择 ImmutableSetMultimap 或 ImmutableListMultimap。它们在多线程环境中非常有用,因为它们是线程安全的,并且提供了高效的内存使用。

6.SynchronizedMultimap

这个实现提供了线程安全的Multimap。它通过在每个方法上添加synchronized关键字来实现线程安全。需要注意的是,迭代操作需要额外的同步措施,因为迭代器的hasNext、next和remove方法之间必须保持同步。

import com.google.common.collect.*;    import java.util.Collections;    public class SynchronizedMultimapExample {      public static void main(String[] args) {          // 创建一个非线程安全的 Multimap          Multimap<String, Integer> multimap = ArrayListMultimap.create();            // 获得一个线程安全的 Multimap          Multimap<String, Integer> synchronizedMultimap = Multimaps.synchronizedMultimap(multimap);            // 在多线程环境中安全地使用 synchronizedMultimap          // 注意:迭代时需要在 synchronized 块中进行          synchronized (synchronizedMultimap) {              for (Map.Entry<String, Integer> entry : synchronizedMultimap.entries()) {                  System.out.println(entry.getKey() + " : " + entry.getValue());              }          }            // 添加元素          synchronizedMultimap.put("test", 1);          synchronizedMultimap.put("test", 2);          synchronizedMultimap.put("another", 3);            // 再次安全地迭代          synchronized (synchronizedMultimap) {              for (String key : synchronizedMultimap.keySet()) {                  System.out.println(key + " : " + synchronizedMultimap.get(key));              }          }      }  }

7.ForwardingMultimap

这是一个装饰器模式的实现,允许你在不修改原始Multimap实现的情况下添加或覆盖行为。你可以通过扩展ForwardingMultimap类并重写需要定制的方法来实现自定义逻辑。

使用 ForwardingMultimap 来创建一个自定义的 Multimap,该 Multimap 在每次添加元素时都会打印一条消息。

import com.google.common.collect.*;    import java.util.Collection;  import java.util.Map;    public class CustomForwardingMultimap<K, V> extends ForwardingMultimap<K, V> {      private final Multimap<K, V> delegate;        public CustomForwardingMultimap(Multimap<K, V> delegate) {          this.delegate = delegate;      }        @Override      protected Multimap<K, V> delegate() {          return delegate;      }        @Override      public Multimap<K, V> putAll(K key, Iterable<? extends V> values) {          System.out.println("Adding values for key: " + key);          return super.putAll(key, values);      }        public static void main(String[] args) {          Multimap<String, Integer> multimap = ArrayListMultimap.create();          CustomForwardingMultimap<String, Integer> customMultimap = new CustomForwardingMultimap<>(multimap);                    customMultimap.putAll("test", Arrays.asList(1, 2, 3));          System.out.println(customMultimap); // Outputs: {test=[1, 2, 3]}      }  }

太强 ! SpringBoot中出入参增强的5种方法 : 加解密、脱敏、格式转换、时间时区处理

太强 ! SpringBoot中优化if-else语句的七种绝佳方法实战

SpringBoot使用EasyExcel并行导出多个excel文件并压缩zip下载
提升编程效率的利器: Google Guava库中双向映射BitMap
从MySQL行格式原理看:为什么开发规范中不推荐NULL?数据是如何在磁盘上存储的?
SpringBoot中使用Jackson实现自定义序列化和反序列化控制的5种方式总结

提升编程效率的利器: Google Guava库之RateLimiter优雅限流

深入JVM逃逸分析原理:且看其如何提高程序性能和内存利用率

必知必会!MySQL索引下推:原理与实战

深入解析JVM内存分配优化技术:TLAB

SpringBoot中基于JWT的双token(access_token+refresh_token)授权和续期方案
SpringBoot中基于JWT的单token授权和续期方案
SpringBoot中Token登录授权、续期和主动终止的方案(Redis+Token)
微服务中token鉴权设计的4种方式总结
提升编程效率的API利器:精通Google Guava库区间范围映射RangeMap
SpringBoot中Jackson控制序列化和反序列化的注解和扩展点总结【收藏版】

SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载

提升编程效率的API利器:精通Google Guava库之IO工具类
提升编程效率的API利器:精通Google Guava库二维映射表Table
提升编程效率的API利器:精通Google Guava库区间范围映射RangeMap
提升编程效率的利器: Google Guava库中双向映射BitMap
提升编程效率的利器: Google Guava库之RateLimiter优雅限流
基于Guava布隆过滤器的海量字符串高效去重实践
加密算法理论总结:分类与典型算法
每个后端开发人员都应该问的发人深省的问题
提升编程效率的API利器:40个示例精通Google Guava库常用工具
MySQL高级优化技巧:使用Hints精准控制查询优化器的选择
每个后端开发人员都应该问的发人深省的问题

Elasticsearch揭秘:高效写入与精准检索的流程原理全解析


关注『 码到三十五 』,日有所获
                     点赞 和 在看 就是最大的支持

码到三十五
主要分享正经的开发技术(原理,架构,实践,源码等),以输出驱动输入;当然偶尔会穿插点生活琐碎,顺便吃个瓜,目的嘛,搞点精准流量,看能不能发发广告。
 最新文章