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

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

Guava库提供了丰富的集合类API,这些API扩展了Java标准库中的集合功能,提供了更多的灵活性和便利性。


在日常开发中,集合类是我们日常编程不可或缺的一部分。Java标准库为我们提供了一套基本的集合类,但在实际项目中,我们往往需要更加灵活和强大的集合功能。这时,Google的Guava库便成为了我们的得力助手。Guava库扩展了Java的集合类,提供了一系列高效、实用且易于使用的集合API。在本文中,我们将深入探索Guava库中常用的集合类API,并了解它们如何提升我们的开发效率。


不可变集合:守护数据的永恒之石

首先,我们要介绍的是Guava提供的不可变集合。在编程中,有时我们需要创建一些一旦初始化就不会再更改的集合。这些集合被称为不可变集合。Guava为我们提供了ImmutableList、ImmutableSet和ImmutableMap等不可变集合的实现。这些集合在创建时确定了内容,并且保证了之后无法修改。这种不可变性带来了诸多好处,比如线程安全、减少错误和提高代码可读性。当你需要一个不会变动的集合时,Guava的不可变集合将是你的最佳选择。

其他API敬请期待后续文章

1. ImmutableList

一个不可变的列表实现,提供了与Java List接口类似的方法,但保证了列表内容不可更改。

2. ImmutableSet

一个不可变的集合实现,与Java Set接口类似,但不允许添加或删除元素。

3. ImmutableMap

一个不可变的映射实现,类似于Java的Map接口,但键值对是固定的,无法修改。

这些不可变集合在创建时确定内容,之后不可更改,有助于编写线程安全的代码。

在pom.xml中添加如下依赖:

<dependency>  
    <groupId>com.google.guava</groupId>  
    <artifactId>guava</artifactId>  
    <version>31.0.1-jre</version> <!-- 请检查是否有更新的版本 -->  
</dependency>

然后,在你的Java代码中使用这些不可变集合:

import com.google.common.collect.ImmutableList;  
import com.google.common.collect.ImmutableSet;  
import com.google.common.collect.ImmutableMap;  
  
public class GuavaImmutableCollectionsExample {  
  
    public static void main(String[] args) {  
        // 创建一个ImmutableList  
        ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");  
        System.out.println("ImmutableList: " + immutableList);  
  
        // 尝试修改ImmutableList(这将导致编译时错误)  
        // immutableList.add("Date"); // 这行代码将无法编译  
  
        // 创建一个ImmutableSet  
        ImmutableSet<Integer> immutableSet = ImmutableSet.of(1, 2, 3, 4, 5);  
        System.out.println("ImmutableSet: " + immutableSet);  
  
        // 尝试修改ImmutableSet(这也将导致编译时错误)  
        // immutableSet.add(6); // 这行代码也无法编译  
  
        // 创建一个ImmutableMap  
        ImmutableMap<String, Integer> immutableMap = ImmutableMap.of("One", 1, "Two", 2, "Three", 3);  
        System.out.println("ImmutableMap: " + immutableMap);  
  
        // 尝试修改ImmutableMap(这同样会导致编译时错误)  
        // immutableMap.put("Four", 4); // 这行代码也无法编译  
  
        // 访问ImmutableMap中的元素  
        Integer value = immutableMap.get("Two");  
        System.out.println("Value associated with 'Two': " + value);  
    }  
}

在上面的代码中,我们展示了如何使用Guava的不可变集合类来创建列表、集合和映射,并尝试(不成功地)修改它们。由于这些集合是不可变的,任何尝试修改它们的操作都会在编译时失败。这对于需要确保数据一致性和线程安全的场景非常有用。


ImmutableTable、ImmutableEnumSet 和 ImmutableEnumMap 在Guava并没有直接提供这些具体的实现。在实际使用中,你应该根据具体的需求选择合适的不可变集合类型,并结合 Java 标准库和 Guava

提供的工具来创建和操作这些集合

4. ImmutableSortedSet 和 ImmutableSortedMap

这两个接口分别表示不可变的排序集合和排序映射。它们提供了根据元素的自然顺序或指定的比较器排序的功能。例如:

import com.google.common.collect.ImmutableSortedSet;  
import com.google.common.collect.ImmutableSortedMap;  
import com.google.common.collect.Ordering;  
  
import java.util.Comparator;  
  
public class GuavaImmutableSortedCollectionsExample {  
  
    public static void main(String[] args) {  
        // 创建一个根据自然顺序排序的ImmutableSortedSet  
        ImmutableSortedSet<Integer> sortedSet = ImmutableSortedSet.copyOf(Ordering.natural(), 10, 5, 15, 20);  
        System.out.println("ImmutableSortedSet (natural order): " + sortedSet);  
  
        // 创建一个根据自定义比较器排序的ImmutableSortedSet  
        Comparator<String> stringComparator = Comparator.comparing(String::length);  
        ImmutableSortedSet<String> sortedSetCustomOrder = ImmutableSortedSet.copyOf(stringComparator, "apple", "banana", "kiwi", "pear");  
        System.out.println("ImmutableSortedSet (custom order): " + sortedSetCustomOrder);  
  
        // 创建一个根据自然顺序排序键的ImmutableSortedMap  
        ImmutableSortedMap<Integer, String> sortedMap = ImmutableSortedMap.of(5, "five", 1, "one", 3, "three");  
        // 注意:上面的of方法会根据键的自然顺序对条目进行排序,但这里我们传入的已经是排序好的键值对  
        System.out.println("ImmutableSortedMap (natural order): " + sortedMap);  
  
        // 创建一个根据自定义比较器排序键的ImmutableSortedMap  
        ImmutableSortedMap<String, Integer> sortedMapCustomOrder =  
                ImmutableSortedMap.orderedBy(stringComparator)  
                        .put("apple", 1)  
                        .put("banana", 2)  
                        .put("kiwi", 3)  
                        .put("pear", 4)  
                        .build();  
        System.out.println("ImmutableSortedMap (custom order): " + sortedMapCustomOrder);  
    }  
}

5. ImmutableMultiset 和 ImmutableMultimap

这两个接口分别表示不可变的多重集和多重映射。多重集允许元素重复出现,而多重映射则允许一个键映射到多个值。例如:

import com.google.common.collect.ImmutableMultiset;  
import com.google.common.collect.ImmutableMultimap;  
import com.google.common.collect.Multiset;  
import com.google.common.collect.Multimap;  
  
import java.util.Arrays;  
  
public class GuavaImmutableMultiCollectionsExample {  
  
    public static void main(String[] args) {  
        // 创建一个ImmutableMultiset  
        ImmutableMultiset<String> multiset = ImmutableMultiset.<String>builder()  
                .addAll(Arrays.asList("apple", "apple", "banana", "orange"))  
                .addCopies("banana", 2) // 添加额外的"banana"元素  
                .build();  
        System.out.println("ImmutableMultiset: " + multiset);  
  
        // 展示元素及其出现次数  
        for (Multiset.Entry<String> entry : multiset.entrySet()) {  
            System.out.println(entry.getElement() + ": " + entry.getCount());  
        }  
  
        // 创建一个ImmutableMultimap  
        ImmutableMultimap<String, Integer> multimap = ImmutableMultimap.<String, Integer>builder()  
                .putAll("fruits", Arrays.asList(1, 2, 3))  
                .putAll("veggies", Arrays.asList(4, 5))  
                .put("fruits", 6) // 添加额外的键值对到"fruits"  
                .build();  
        System.out.println("ImmutableMultimap: " + multimap);  
  
        // 展示键及其对应的值  
        for (String key : multimap.keySet()) {  
            System.out.println(key + " => " + multimap.get(key));  
        }  
    }  
}

在这个示例中,我们展示了如何创建 ImmutableMultiset 和 ImmutableMultimap。ImmutableMultiset 允许元素重复出现,并且我们可以使用 addCopies 方法来添加指定数量的元素。ImmutableMultimap 允许一个键映射到多个值。

输出将类似于以下内容:

ImmutableMultiset: [apple x 2, banana x 3, orange]  
apple: 2  
banana: 3  
orange: 1  
ImmutableMultimap: {fruits=[1, 2, 3, 6], veggies=[4, 5]}  
fruits => [1, 2, 3, 6]  
veggies => [4, 5]

请注意,ImmutableMultiset 和 ImmutableMultimap 都是不可变的,这意味着一旦创建,您就不能向它们添加或删除元素。如果您需要一个可变的版本,可以使用 Multiset 和 Multimap 接口的其他实现,例如 HashMultiset 和 ArrayListMultimap,然后在需要的时候使用 ImmutableMultiset.copyOf(multiset) 或 ImmutableMultimap.copyOf(multimap) 来创建不可变副本。

6. ImmutableTable

ImmutableTable 是 Google Guava 库中提供的一种不可变的三维表格数据结构。它类似于 ImmutableMap,但是它可以存储两个键和一个值的映射关系,可以看作是一种特殊的集合。它允许你通过行和列来访问元素。例如:

import com.google.common.collect.ImmutableTable;  
import com.google.common.collect.Table;  
  
import java.util.Map;  
  
public class GuavaImmutableTableExample {  
  
    public static void main(String[] args) {  
        // 创建一个ImmutableTable  
        ImmutableTable<String, String, Integer> table = ImmutableTable.<String, String, Integer>builder()  
                .put("apple", "red", 1)  
                .put("apple", "green", 2)  
                .put("banana", "yellow", 3)  
                .build();  
  
        // 输出整个表格  
        System.out.println("ImmutableTable: " + table);  
  
        // 获取某个键对应的行映射  
        Map<String, Integer> appleRow = table.row("apple");  
        System.out.println("Apple row: " + appleRow);  
  
        // 获取某个列对应的映射  
        Map<String, Integer> redColumn = table.column("red");  
        System.out.println("Red column: " + redColumn);  
  
        // 获取某个具体的值  
        Integer valueOfAppleRed = table.get("apple", "red");  
        System.out.println("Value of apple red: " + valueOfAppleRed);  
  
        // 判断是否包含某个键值对  
        boolean containsAppleGreen = table.contains("apple", "green");  
        System.out.println("Contains apple green: " + containsAppleGreen);  
  
        // 尝试修改(注意:这会失败,因为ImmutableTable是不可变的)  
        // table.put("apple", "red", 42); // 这行代码会导致编译错误  
    }  
}

请注意,在上面的例子中,ImmutableTable.copyOf 方法实际上并不直接存在于 Guava 库中。相反,你应该使用 Tables.immutableTable 方法,但这个方法接受的是一个已经存在的表格,并返回一个不可变的视图。然而,由于 Guava 没有直接提供一个简单的方法来创建一个全新的不可变表格,通常的做法是先创建一个可变的表格,然后将其转换为一个不可变的视图。

7. ImmutableBiMap

表示不可变的、双向映射的集合。它同时提供了键到值和值到键的映射关系,并且保证了键和值的唯一性。与 ImmutableMap 类似,它也不允许添加、删除或更改映射关系。

import com.google.common.collect.ImmutableBiMap;  
import com.google.common.collect.ImmutableBiMap.Builder;  
  
public class ImmutableBiMapExample {  
  
    public static void main(String[] args) {  
        // 使用 Builder 创建 ImmutableBiMap  
        Builder<String, Integer> builder = ImmutableBiMap.builder();  
        builder.put("one", 1);  
        builder.put("two", 2);  
        builder.put("three", 3);  
        ImmutableBiMap<String, Integer> biMap = builder.build();  
  
        // 输出整个双向映射  
        System.out.println("ImmutableBiMap: " + biMap);  
  
        // 通过键获取值  
        Integer valueOne = biMap.get("one");  
        System.out.println("Value for 'one': " + valueOne);  
  
        // 通过值获取键(这是 BiMap 的特点)  
        String keyForValueTwo = biMap.inverse().get(2);  
        System.out.println("Key for value 2: " + keyForValueTwo);  
  
        // 尝试修改(注意:这会失败,因为 ImmutableBiMap 是不可变的)  
        // biMap.put("four", 4); // 这行代码会导致编译错误  
  
        // 尝试使用已存在的值作为键进行插入(也会失败,因为值也必须唯一)  
        // builder.put("four", 2); // 这同样会导致错误,即使你试图在 build() 之后再做  
    }  
}

在上面的示例中,我使用了 ImmutableBiMap.Builder 来构建一个 ImmutableBiMap 实例。这个双向映射允许你通过键来查找值,也可以通过值来查找键(使用 inverse() 方法)。由于 ImmutableBiMap 是不可变的,任何试图修改它的操作(如 put 方法)都会导致编译时错误。


请注意,在构建 ImmutableBiMap 之后,你不能再使用 builder 添加或修改条目,因为 builder 已经在 build() 调用时被消耗掉了。如果你需要另一个不同的 ImmutableBiMap,你必须创建一个新的 Builder 实例。

此外,ImmutableBiMap 保证键和值的唯一性,所以每个键映射到一个唯一的值,每个值也映射到一个唯一的键。这意味着你不能在 ImmutableBiMap 中有重复的键或值。

总结

这些不可变集合的 API 都具有相似的特点,即不允许修改集合内容,提供了线程安全的访问方式,并且在创建时就需要确定集合的元素。这些集合类型在 Guava 库中被广泛使用,可以帮助开发者编写更加健壮和可维护的代码。


需要注意的是,所有 Guava 不可变集合的实现都不接受 null 值。如果需要在不可变集合中使用 null,可以考虑使用 JDK 中的 Collections.unmodifiableXXX 方法。


以上是关于 Google Guava 不可变集合 API 的简要介绍,更多详细信息和用法可以参考 Guava 官方文档。

相关文章
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
48 3
|
4天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
25天前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
41 10
|
25天前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
41 8
|
25天前
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
34 4
|
1月前
|
存储 缓存 开发者
Python编程中的装饰器深度解析
本文将深入探讨Python语言的装饰器概念,通过实际代码示例展示如何创建和应用装饰器,并分析其背后的原理和作用。我们将从基础定义出发,逐步引导读者理解装饰器的高级用法,包括带参数的装饰器、多层装饰器以及装饰器与类方法的结合使用。文章旨在帮助初学者掌握这一强大工具,同时为有经验的开发者提供更深层次的理解和应用。
36 7
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
安全 程序员 API
|
1月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。

热门文章

最新文章

推荐镜像

更多