提升编程效率的利器: 解析Google Guava库之集合篇Multimap(二)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 提升编程效率的利器: 解析Google Guava库之集合篇Multimap(二)

一、Multimap 的核心特点

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

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


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

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


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


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


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

二、Multimap常用方法

直接上代码吧,下面是一个结合Guava库中Multimap常用方法的示例:

首先,我们需要添加Guava库到项目中,以便使用Multimap。如果你使用的是Maven,你可以在pom.xml中添加以下依赖:

<dependency>  
    <groupId>com.google.guava</groupId>  
    <artifactId>guava</artifactId>  
    <version>30.1-jre</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允许存储重复的键,并且每个键可以关联多个值,因此上述代码中的输出可能会根据键的插入和删除顺序而有所不同。此外,keySet、values和entries返回的集合视图会随Multimap的更改而动态更新。


在实际应用中,你可以根据具体需求选择使用Multimap的哪种实现(如ArrayListMultimap、HashMultimap、LinkedListMultimap等),并调整上述示例以满足你的场景。

三、常用的 Multimap 实现

Guava 提供了多种 Multimap 的实现,每种实现都有其特定的用途和性能特点。以下是一些常用的 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"));  
    }  
}

这个示例展示了如何使用不同类型的Multimap来存储和处理社交网络中的数据。我们使用了put,putAll,get,containsEntry,keys,entries和remove等方法来操作Multimap。每种Multimap都有其特定的用途,例如ArrayListMultimap和LinkedHashMultimap保留了插入顺序,而HashMultimap则提供了快速的无序查找,TreeMultimap则按键排序。


5. ImmutableSetMultimap 和 ImmutableListMultimap

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


示例请参考:提升编程效率的利器: 解析Google Guava库之集合篇(一)

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));  
            }  
        }  
    }  
}

在上面的代码中,我们先创建了一个非线程安全的 ArrayListMultimap,然后使用 Multimaps.synchronizedMultimap 方法获得了一个线程安全的 Multimap。我们展示了如何在多线程环境中安全地使用这个 Multimap,包括在迭代时需要在 synchronized 块中进行以避免并发修改异常。


请注意,Multimaps.synchronizedMultimap 返回的 Multimap 并不是 SynchronizedMultimap 类型,而是原始 Multimap 的一个线程安全视图。在实际代码中,你不需要(也不能)直接实例化一个 SynchronizedMultimap,而是应该使用 Multimaps.synchronizedMultimap 方法来获取线程安全的视图。


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]}  
    }  
}

在上面的代码中,我们创建了一个 CustomForwardingMultimap 类,它扩展了 ForwardingMultimap。我们通过覆盖 putAll 方法来添加自定义行为(在这种情况下,是打印一条消息)。所有其他方法(如 get,size 等)将直接委托给底层 Multimap 实现。

四、总结

Guava 的 Multimap 提供了一种灵活和方便的方式来处理一对多的映射关系。通过选择合适的实现,我们可以满足不同的需求,并获得更好的性能和易用性。在使用 Multimap 时,我们应该根据具体的需求和性能要求来选择合适的实现,并且充分利用其提供的视图和操作方法来简化代码和提高效率。


相关文章
|
25天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
34 3
|
1月前
|
数据处理 开发者 Python
Python 高级编程:深入解析 CSV 文件读取
在Python中,读取CSV文件是数据处理的重要环节。本文介绍了两种高效方法:一是利用pandas库的`read_csv`函数,将CSV文件快速转换为DataFrame对象,便于数据操作;二是通过csv模块的`csv.reader`按行读取CSV内容。此外,还涉及了如何选取特定列、解析日期格式、跳过指定行以及分块读取大文件等高级技巧,帮助开发者更灵活地处理各种CSV文件。参考链接:&lt;https://www.wodianping.com/app/2024-10/48782.html&gt;。
90 6
|
13天前
|
安全 程序员 API
|
10天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
9天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
21天前
|
Java 开发者 UED
Java编程中的异常处理机制解析
在Java的世界里,异常处理是确保程序稳定性和可靠性的关键。本文将深入探讨Java的异常处理机制,包括异常的类型、如何捕获和处理异常以及自定义异常的创建和使用。通过理解这些概念,开发者可以编写更加健壮和易于维护的代码。
|
29天前
|
Java 关系型数据库 MySQL
【编程基础知识】Eclipse连接MySQL 8.0时的JDK版本和驱动问题全解析
本文详细解析了在使用Eclipse连接MySQL 8.0时常见的JDK版本不兼容、驱动类错误和时区设置问题,并提供了清晰的解决方案。通过正确配置JDK版本、选择合适的驱动类和设置时区,确保Java应用能够顺利连接MySQL 8.0。
123 1
|
1月前
|
Java
【编程基础知识】《Java 基础探秘:return、break、continue、label、switch 与 enum 的深度解析》
本文深入解析了 Java 中的 return、break、continue、label、switch 和 enum 等基础概念,通过代码示例和流程图,帮助读者理解这些控制结构和枚举类型在编程中的应用,提升编程能力。
23 3
|
9天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
10 0
|
1月前
|
Java
【编程基础知识】《Java 中的神秘利器:this 关键字深度解析》
《Java 中的神秘利器:this 关键字深度解析》深入探讨了 Java 中 this 关键字的作用、用法及应用场景。文章详细解释了 this 如何指向当前对象、区分成员变量和局部变量、调用构造函数、实现方法链式调用和传递当前对象。通过阅读本文,读者将全面掌握 this 关键字的巧妙应用,提升 Java 编程技能。
29 2

推荐镜像

更多