老司机阿粉带你玩转 Guava 集合类

本文详细介绍Guava集合类的使用方法,包括Multiset、Multimap、BiMap等新集合类型,以及不可变集合和集合工具类的特性。通过实例展示如何使用Guava简化Java集合操作,提高代码质量和效率。

每天早上七点三十,准时推送干货

日常开发中,阿粉经常需要用到 Java 提供集合类完成各种需求。Java 集合类虽然非常强大实用,但是提供功能还是有点薄弱。

举个例子,阿粉最近接到一个需求,从输入一个文档中,统计一个关键词出现的次数。代码如下:

虽然这个需求使用 Map 可以轻松搞定,但是阿粉还是觉得这种写法有点笨拙,如果没有判空,将会导致 NPE 异常。

如果很多地方需要功能,我们就可以抽象出来,将其封装成工具类。

不过上面的功能大家就不需要自己封装,一款来自 Google 开源工具类-Guava,可以轻松的解决上面的统计问题。

Guava 介绍

Guava 是一款 Google 开源工具类,包含许多 Google 内部 Java 项目依赖的核心类。Guava 扩展 Java 基础类工程,比如集合,并发等,也增加一些其他强大功能,比如缓存,限流等功能。

另外 Guava 推出一些类,如 Optional,甚至被 Java 开发者学习,后续增加到 JDK 中。

目前 Guava Github 仓库已有 36k star,可以见到 Guava 受欢迎程度。


Guava 核心功能包括多个模块,今天阿粉主要带大家玩转 Guava 集合类。

扩展集合类

Guava 创造很多 JDK 没有,但是我们日常却明显有用的新集合类型。这些新类型使用 JDK 集合接口规范,所以使用方法与 JDK 集合框架差不多,并没有增加很多使用难度。

Multiset

阿粉第一次见到 Multiset 这个类,还以为是 Set 接口子类。实际上此『Set』,仅仅只是数学上集合概念。

Multiset 继承 JDK Collection 接口,我们可以多次增加相同的元素,另外 Multiset 最大特定将会为元素计数,我们可以将它类似等同为 Map<E, Integer>

使用 Multiset可以轻松解决开头的问题。

使用 Multiset 简化了代码,并且再也不用担心新 NPE 的问题。

跟 JDK 集合类一样,Multiset也有许多子类。

来源于 Github

这里阿粉提醒一下大家,虽然上面说过我们可以将 Multiset<E> 看做 Map<E, Integer>,但是 Multiset 可不是 Map 的子类,它可是 血统纯正的 Collection 子类。

Multimap

阿粉有时会在业务需求中使用 Map<String,List<Integer>实现下面的需求。

1a->[1,2,3] b->4,c->[6,5]

使用 Map + List 这种结构比较笨拙,并且代码实现也比较繁琐。Multimap 正式 Guava 中解决这种问题的新出的一个雷。

使用 Multimap 实现代码如下:

这里阿粉使用 Multimap 子类 HashMultimap,其行为类似为 Map<K,Set<V>>,也就是说 Value 对应的集合内部元素不能重复。如果需要保存的重复的元素我们可以使用 ArrayListMultimap

Multimap还有其他子类,如图所示:

来源于 Github

BiMap

BiMap 可以用来实现键值对的双向映射需求,这样我们就可以通过 Key 查找对对应的 Value,也可以使用 Value 查找对应的 Key

这个需求如果使用 Map 实现,我们就不得不使用两个 Map,维护双向关系,并且任何改动还要保持同步。

使用 BiMap 修改上面的代码:

这里需要注意,BiMap#put方法不能加入重复元素, 若加入,将会抛错。如果若特定值一定要替换,可以使用 BiMap#forcePut代替。

敲黑板,这个知识点记下来。阿粉使用过程中,就踩过这个坑。

同样的 BiMap 也有各种实现类:

来源于 Github

其他扩展集合类

Guava 另外还提供其他集合类,不过这些类使用起来有点复杂,阿粉还未在业务代码中使用过,这里简单提下,感兴趣同学可以深入了解一下。

  • Table

  • ClassToInstanceMap

  • RangeSet

  • RangeMap

集合工具类

除了上面提到的新集合类以外,Guava 提供通用的工具类:

来源于 Github

这些工具类需对使用的方法,我们可以快速创建集合,分割集合,转化集合等。

快速创建集合实例

使用工具类,我们可以快速创建集合。例如:

1List<String> list=Lists.newArrayList();
2Set<String> set=Sets.newHashSet();
3Map<String,String> map=Maps.newHashMap();

相比于 new 集合方法,Guava 方法创建方式更加简单。

1List<String> list=new ArrayList<String>();
2Set<String> set=new HashSet<String>();
3Map<String,String> map=new HashMap<String, String>();

Guava 工具类智能推导 List 泛型,再也不用两侧都重复写泛型了。

另外还可以指定集合类的初始化大小。

Lists.transform

Lists#transform方法可以替代繁琐 for 循环,将元素转化,创建一个新集合类。

不过使用这个方法我们要注意一点。

Lists#transform 内部使用懒加载的机制,只有在调用获取的元素的时候,如 result.get 才会真正使用 Function 从源 List 获取元素,做相应的转化。每次获取元素都将会使用 function 进行转化。

所以使用 Lists#transform 得到 List 仅仅只是源 List 一个视图,任何对源 List 的元素修改,都将会被反应到创建之后的 List 。任何对创建之后 List 中的元素进行修改,都不会生效。下次再次读取元素时,将会发现相应修改的丢失了。。。

阿粉之前就踩过这个坑,如果你有这种需求,可以使用以下方式创建一个新集合:

JDK8 之前版本,阿粉经常使用该方法转化 List 中的元素。不过你如果使用 JDK8,阿粉还是推荐使用 Stream 流式编程。

交集并集差集

Sets 提供几个方法,可以快速求出两个 Set 集合的交集,并集以及差集。

不可变集合

不可变(Immutable)集合,顾名思义集合不可以被修改。初始创建不可变集合时吗,需要传入数据源,创建完成之后,集合就再也不能修改,增加,删除元素,否则将会报错。

这是一种防御性策略,防止集合在后续操作中被修改,从而引发问题。

不可变集合优点在于:

  • 由于不可变集合仅仅只能读,多线程并发天然安全

  • 由于不可变集合固定不变,可以将其当做常量安全,不用单线其他人修改

  • 不可变集合占用更少内存空间

  • 不可变集合不可以被修改,所以不用担心其他程序任意修改集合

Guava 不可变集合支持 JDK 所有集合接口:

我们可以使用如下几种方式创建不可变集合,以 ImmutableList 为例:

总结

这篇文章阿粉带大家学习开源工具 Guava 集合的相关类使用方法,日常开发中我们善于使用这些工具类,不要自己重复造轮子。

本篇文章仅仅只是介绍 Guava 一小部分功能,还有很对功能,阿粉也觉得很好用在。这里推荐大家去查看 Guava 官方 wiki,查看具体使用方法。

如果大家还想知道其他开源工具类,给阿粉点个赞,下次给大家带来十分好用开源工具类~

< END >

如果大家喜欢我们的文章,欢迎大家转发,点击在看让更多的人看到。也欢迎大家热爱技术和学习的朋友加入的我们的知识星球当中,我们共同成长,进步。

往期精彩回顾

诡异的并发之可见性
面试官提到的 AVL 树,到底是个啥

重点丨什么是双重检查锁模式?以及为何需要 volatile 关键字?

<think> 嗯,用户想了解Guava库中的集合类信息。从对话历史看,用户已经提供了一些引用片段作为背景资料,我需要整理这些信息并给出系统化的解答。 用户提供的引用很有价值:引用[1]和[2]分别提到Sets和Lists工具类,引用[3]则概述了Guava集合工具的整体框架,包括Lists/Sets/Maps三大核心工具类,以及Iterables、Ordering等扩展功能。引用[4]展示了与Java标准集合创建的对比,引用[5]则提到RangeMap这种特殊集合。 用户的核心需求很明确——希望系统了解Guava集合类的体系。我会从三个层面组织回答:首先是核心工具类(Lists/Sets/Maps),其次是功能扩展类(Iterables/Ordering等),最后是特殊集合类型(RangeSet/EvictingQueue)。需要特别注意两点:一是强调Guava相比Java标准库的优势(如不可变集合、流式操作),二是用具体代码示例说明用法,比如引用[4]展示的简洁创建方式就很直观。 在回答结构上,考虑先总述Guava集合工具的价值(如引用[3]所说"增强功能性和易用性"),再分模块详解。对于特殊集合类型,要说明适用场景,比如RangeMap适合区间映射的场景。最后准备的相关问题可以引导用户深入了解特定工具类或性能优化实践。 值得注意的细节:不可变集合的线程安全特性要重点说明,RangeSet/RangeMap的非合并特性(引用[5])容易产生误解需要澄清,EvictingQueue的自动驱逐机制也很实用。代码示例采用Java但保持简洁,避免过多语法细节干扰主干认知。 </think> 好的,我们来详细介绍 Google Guava 库中的集合类(Collections)。Guava 极大地扩展了 Java 标准集合框架(Java Collections Framework, JCF),提供了更强大、更易用、更高效的集合操作工具和新的集合类型。 Guava集合工具主要体现在以下几个方面: 1. **核心工具类 (Utility Classes):** * **`Lists`**: 专注于 `List` 及其子类的实用操作。 * **创建:** 提供比 `new ArrayList<>()` 更简洁的方式创建可变 (`newArrayList()`) 和不可变 (`immutableListOf()`, `copyOf()`) 列表。例如: ```java List<String> names = Lists.newArrayList("Alice", "Bob", "Charlie"); // 可变 ImmutableList<String> fixedNames = ImmutableList.of("Alpha", "Beta", "Gamma"); // 不可变 ``` * **转换:** `transform(list, function)` 对列表每个元素应用函数生成新列表。 * **分区/分块:** `partition(list, size)` 将大列表分割成指定大小的子列表。 * **反转:** `reverse(list)` 返回列表的反转视图。 * **字符处理:** `charactersOf(string)` 将字符串视为字符列表。 * **`Sets`**: 专注于 `Set` 及其子类的实用操作。[^1] * **创建:** 提供 `newHashSet()`, `newLinkedHashSet()`, `newTreeSet()`, `immutableSetOf()`, `copyOf()` 等。 * **集合运算:** 核心价值所在! * `union(set1, set2)`:并集。 * `intersection(set1, set2)`:交集。 * `difference(set1, set2)`:差集(属于 set1 但不属于 set2)。 * `symmetricDifference(set1, set2)`:对称差集(仅在其中一个集合中的元素)。 * **过滤:** `filter(set, predicate)` 返回满足条件的元素子集视图。 * **笛卡尔积:** `cartesianProduct(set1, set2, ...)` 返回多个集合的笛卡尔积。 * **`Maps`**: 专注于 `Map` 及其子类的实用操作。 * **创建:** `newHashMap()`, `newLinkedHashMap()`, `newTreeMap()`, `immutableMapOf()`, `copyOf()` 等。 * **转换:** `transformValues(map, function)` 对 Map 的每个值应用函数生成新 Map 视图;`transformEntries(map, function)` 转换键值对。 * **过滤:** `filterKeys(map, predicate)`, `filterValues(map, predicate)`, `filterEntries(map, predicate)`。 * **唯一索引:** `uniqueIndex(iterable, keyFunction)` 根据元素属性生成键唯一的 Map。 * **差值:** `difference(map1, map2)` 比较两个 Map 的差异(键值对的增删改)。 * **`BiMap` 工具:** 提供操作双向映射的工具。 2. **功能增强工具类 (Enhanced Utilities):** * **`Iterables` / `Iterators`**: 提供通用、强大的操作,适用于任何实现了 `Iterable` 或 `Iterator` 接口的对象,包括标准集合和自定义集合。[^3] * **链式操作:** `filter(iterable, predicate)`, `transform(iterable, function)`, `concat(iterable...)` 连接多个可迭代对象, `limit(iterable, maxSize)`, `skip(iterable, number)`, `cycle(iterable)` 循环迭代。 * **查找与判断:** `contains(iterable, element)`, `frequency(iterable, element)`, `any(iterable, predicate)`, `all(iterable, predicate)`, `find(iterable, predicate)`, `getFirst(iterable, default)`, `getLast(iterable, default)`。 * **分组:** `groupBy(iterable, keyFunction)` 根据键函数分组元素。 * **批量操作:** `addAll(collection, iterable)` 高效添加。 * **元素计数:** `size(iterable)` 高效计算元素个数(尽可能避免遍历)。 * **`Ordering`**: Guava 强大的流畅比较器 (`Comparator`) 实现框架。[^3] * **创建:** `natural()` (自然顺序), `usingToString()` (toString 顺序), `from(comparator)` (包装现有比较器), `explicit(values)` (指定固定顺序)。 * **链式调用:** `reverse()`, `nullsFirst()`, `nullsLast()`, `compound(secondaryComparator)`(主比较器相同时使用次比较器),`onResultOf(function)`(先对元素应用函数再比较)。 * **操作集合:** `greatestOf(iterable, k)` 获取最大的 k 个元素,`isOrdered(iterable)` 检查是否有序,`sortedCopy(iterable)`, `immutableSortedCopy(iterable)`。 ```java Ordering<Person> ordering = Ordering.natural().onResultOf(Person::getAge) // 按年龄升序 .nullsLast() // 将年龄为null的排到最后 .compound(Ordering.natural().onResultOf(Person::getName)); // 年龄相同按姓名升序 List<Person> sortedPeople = ordering.immutableSortedCopy(people); ``` 3. **新型集合类型 (New Collection Types):** * **不可变集合 (`ImmutableList`, `ImmutableSet`, `ImmutableMap`, `ImmutableXXX`):** [^3][^4] * **核心特性:** 创建后内容绝对不可修改(线程安全、内存效率高、可作为常量安全使用)。 * **创建方式:** 使用 `of()`, `copyOf()`, `builder()` 模式。`ImmutableList.copyOf(someList)` 是常用方式。 * **优势:** 安全性、效率、防御式编程。 * **`Multiset`**: 可以统计元素出现次数的集合(像 `Bag` 或计数的 `Set`)。 * **实现:** `HashMultiset`, `TreeMultiset`, `LinkedHashMultiset`, `ImmutableMultiset`。 * **核心方法:** `count(element)` 获取元素计数,`add(element, occurrences)`, `remove(element, occurrences)`, `elementSet()` 获取唯一元素集。 * **`Multimap`**: 一个键可以映射到多个值的映射(类似 `Map<K, List<V>>` 或 `Map<K, Set<V>>`,但更易用)。 * **实现:** `ArrayListMultimap` (值存储在 `ArrayList` 中), `HashMultimap` (值存储在 `HashSet` 中), `LinkedListMultimap` (保留插入顺序), `TreeMultimap` (键和值排序), `ImmutableMultimap`。 * **核心方法:** `put(key, value)`, `get(key)` 返回值的集合(不是单个值!),`containsKey(key)`, `containsEntry(key, value)`, `remove(key, value)`, `removeAll(key)`, `replaceValues(key, values)`。 * **`BiMap` (双向映射):** 保证键和值都是唯一的,提供了 `inverse()` 方法来获取值到键的反向视图。 * **实现:** `HashBiMap`, `EnumBiMap`, `EnumHashBiMap`, `ImmutableBiMap`。 * **核心约束:** `put(key, value)` 时,如果值已存在(即使对应不同的键)会抛出 `IllegalArgumentException`。可使用 `forcePut(key, value)` 强制替换(慎用)。 * **`Table`**: 类似于有两个键(行键和列键)的映射(类似 `Map<RowKey, Map<ColumnKey, Value>>`)。 * **实现:** `HashBasedTable`, `TreeBasedTable`, `ArrayTable` (行列固定且已知时高效)。 * **核心方法:** `put(rowKey, columnKey, value)`, `get(rowKey, columnKey)`, `row(rowKey)` 返回对应行的 `Map<ColumnKey, Value>`, `column(columnKey)` 返回对应列的 `Map<RowKey, Value>`。 * **`RangeSet` / `RangeMap`**: 处理区间(`Range`)的集合。[^5] * **`RangeSet`**: 表示一组不相交、非空的区间集合。支持高效的区间查询(`contains(value)`, `encloses(range)`)和区间操作(`add(range)`, `remove(range)`, `complement()` 补集)。 * **`RangeMap`**: 将不相交、非空的区间(`Range`)映射到某个值。查询一个点落在哪个区间(`get(key)`)或者哪个区间包含某个值(`getEntry(key)`)非常高效。**_不会合并相邻但映射值相同的区间_**。[^5] ```java RangeMap<Integer, String> gradeRange = TreeRangeMap.create(); gradeRange.put(Range.closed(90, 100), "A"); gradeRange.put(Range.closedOpen(80, 90), "B"); // 80 <= score < 90 gradeRange.put(Range.closedOpen(70, 80), "C"); String grade = gradeRange.get(85); // 返回 "B" ``` * **`EvictingQueue`**: 一种定长的、具有自动驱逐功能的队列。[^3] * **特性:** 当队列满时,再添加新元素会导致队列头部的元素被自动移除(先进先出淘汰)。 * **创建:** `EvictingQueue.create(maxSize)`。 * **适用场景:** 实现简单的滑动窗口、缓存、最近 N 项记录等。 **总结:** Guava集合部分是其最核心、最常用的模块之一。它通过: 1. **工具类 (`Lists`, `Sets`, `Maps`, `Iterables`, `Iterators`, `Ordering`):** 极大地简化了标准集合的创建、转换、过滤、运算(尤其是 Sets)和排序操作,代码更简洁高效。 2. **不可变集合:** 提供安全、高效、线程安全的常量集合实现。 3. **新型集合 (`Multiset`, `Multimap`, `BiMap`, `Table`, `RangeSet`, `RangeMap`, `EvictingQueue`):** 引入了标准库中没有的、但实践中非常实用的数据结构,解决了特定场景下的复杂需求(如计数、一对多映射、双向映射、二维表、区间查询、固定大小缓存队列等)。 使用 Guava 集合工具类和新集合类型,可以显著提升 Java 代码在处理集合数据时的可读性、简洁性和性能。[^1][^2][^3][^4][^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值