深入解析 Guava Collection 工具类:提升 Java 集合操作效率
引言
任何熟悉 JDK 集合框架的程序员都深知 java.util.Collections 工具类的价值。Google Guava 在此基础上提供了更多强大的静态工具方法,适用于所有集合类型。这些工具类是 Guava 库中最成熟、最受欢迎的部分之一。本文将深入探讨 Guava 提供的各类集合工具,包括 Lists, Sets, Maps, Multisets, Multimaps, Tables 以及 Iterables 等,帮助您编写更简洁、高效的 Java 代码。
核心工具类概览
Guava 按照集合接口类型将工具方法分组到对应的工具类中:
| 接口类型 | 来源 | Guava 工具类 |
|---|---|---|
| Collection | JDK | Collections2 |
| List | JDK | Lists |
| Set | JDK | Sets |
| Map | JDK | Maps |
| Queue | JDK | Queues |
| Multiset | Guava | Multisets |
| Multimap | Guava | Multimaps |
| BiMap | Guava | Maps |
| Table | Guava | Tables |
注意:transform、filter 等功能性操作请参考 Guava 的函数式编程相关文章。
一、静态构造方法
1.1 类型推断简化
在 JDK 7 之前,创建泛型集合需要重复书写类型信息:
List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();
Guava 提供了静态工厂方法利用泛型推断简化代码:
List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();
虽然 JDK 7 的菱形操作符(<>)使这些方法显得过时,但 Guava 的静态工厂方法在以下场景仍然有用:
1.2 预分配容量
通过具名工厂方法提高代码可读性:
// 精确容量
List<Type> exactly100 = Lists.newArrayListWithCapacity(100);
// 预期大小(内部会计算合适的容量)
List<Type> approx100 = Lists.newArrayListWithExpectedSize(100);
Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);
1.3 初始化元素
一行代码创建并初始化集合:
Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");
最佳实践:通常推荐使用不可变集合(Immutable Collections)替代可变集合。
二、Iterables:惰性处理的 Iterable 工具
Guava 倾向于提供接受 Iterable 而非 Collection 的工具方法,因为许多"集合"可能并非存储在内存中(如从数据库或远程服务获取的数据),无法支持 size() 等操作而不实际加载所有元素。
2.1 核心特性
- 惰性计算:大多数
Iterables方法只在必要时才推进底层迭代 - 视图返回:返回
Iterable的方法通常返回惰性计算的视图,而非在内存中构建新集合 - FluentIterable:Guava 12+ 提供的流式 API,包装
Iterable提供链式操作
2.2 常用通用方法
| 方法 | 描述 | 示例 |
|---|---|---|
concat(Iterable<Iterable>) | 连接多个 Iterable 的惰性视图 | Iterables.concat(list1, list2) |
frequency(Iterable, Object) | 返回对象出现次数 | Iterables.frequency(items, target) |
partition(Iterable, int) | 将 Iterable 分区为指定大小的块 | Iterables.partition(data, 10) |
getFirst(Iterable, T default) | 获取第一个元素或默认值 | Iterables.getFirst(items, defaultValue) |
getLast(Iterable) | 获取最后一个元素 | Iterables.getLast(linkedSet) |
getOnlyElement(Iterable) | 获取唯一元素(失败快) | Iterables.getOnlyElement(singletonSet) |
limit(Iterable, int) | 限制返回元素数量 | Iterables.limit(source, 100) |
// 连接多个列表
Iterable<Integer> concatenated = Iterables.concat(
Ints.asList(1, 2, 3),
Ints.asList(4, 5, 6)
);
// 安全获取唯一元素
String theElement = Iterables.getOnlyElement(thisSetIsDefinitelyASingleton);
2.3 类集合操作
这些操作在输入为 Collection 时会委托给对应的 Collection 方法以提高效率:
// 添加所有元素
Iterables.addAll(targetCollection, sourceIterable);
// 检查包含
boolean contains = Iterables.contains(iterable, element);
// 获取大小(对 Collection 优化)
int size = Iterables.size(iterable);
// 转换为数组
String[] array = Iterables.toArray(iterable, String.class);
2.4 FluentIterable 流式 API
FluentIterable.from(source)
.filter(predicate)
.transform(function)
.limit(10)
.toImmutableList();
支持直接转换为不可变集合:
toImmutableList()toImmutableSet()toImmutableSortedSet(Comparator)
三、Lists:列表专用工具
3.1 列表视图操作
// 分区:将列表划分为固定大小的块
List<List<Integer>> parts = Lists.partition(countUp, 2);
// 结果:{{1, 2}, {3, 4}, {5}}
// 反转:返回列表的反转视图
List<Integer> countDown = Lists.reverse(theList);
// 注意:对于不可变列表,建议使用 ImmutableList.reverse()
3.2 静态工厂方法
Lists 提供多种 ArrayList 和 LinkedList 的创建方法:
- 基础创建:
newArrayList(),newLinkedList() - 带元素创建:
newArrayList(E...),newLinkedList(Iterable) - 容量优化:
newArrayListWithCapacity(int),newArrayListWithExpectedSize(int) - 从迭代器创建:
newArrayList(Iterator),newLinkedList(Iterable)
四、Comparators:比较器工具
4.1 查找最大值/最佳实践
| 比较类型 | 2个实例 | 多个实例 |
|---|---|---|
| 基本数值类型 | Math.max(a, b) | Ints.max(a, b, c...), Longs.max(...) |
| Comparable 实例 | Comparators.max(a, b) | Collections.max(asList(a, b, c)) |
| 自定义 Comparator | Comparators.max(a, b, cmp) | Collections.max(asList(a, b, c), cmp) |
建议:静态导入相关方法以简化代码,如
max(asList(a, b, c))
五、Sets:集合论操作
5.1 集合论运算
Guava 提供标准集合论操作,返回 SetView(可直接作为 Set 使用):
Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
Set<String> primes = ImmutableSet.of("two", "three", "five", "seven");
// 交集
SetView<String> intersection = Sets.intersection(primes, wordsWithPrimeLength);
// 包含:"two", "three", "seven"
// 可转换为不可变副本以提高重复使用效率
return intersection.immutableCopy();
支持的操作:
union(Set, Set)- 并集intersection(Set, Set)- 交集difference(Set, Set)- 差集symmetricDifference(Set, Set)- 对称差集
5.2 其他集合工具
// 笛卡尔积
Set<List<String>> product = Sets.cartesianProduct(animals, fruits);
// 结果:{{"gerbil", "apple"}, {"gerbil", "orange"}, ...}
// 幂集(所有子集)
Set<Set<String>> animalSets = Sets.powerSet(animals);
// 结果:{{}, {"gerbil"}, {"hamster"}, {"gerbil", "hamster"}}
六、Maps:映射工具
6.1 uniqueIndex:唯一索引构建
将具有唯一属性的对象集合转换为基于该属性的 Map:
ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings,
new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
});
注意:如果索引不唯一,请使用
Multimaps.index
6.2 difference:地图差异比较
比较两个 Map 的所有差异:
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> right = ImmutableMap.of("b", 2, "c", 4, "d", 5);
MapDifference<String, Integer> diff = Maps.difference(left, right);
diff.entriesInCommon(); // {"b" => 2}
diff.entriesDiffering(); // {"c" => (3, 4)}
diff.entriesOnlyOnLeft(); // {"a" => 1}
diff.entriesOnlyOnRight(); // {"d" => 5}
6.3 BiMap 工具
BiMap 的工具方法也在 Maps 类中:
synchronizedBiMap(BiMap)- 同步包装unmodifiableBiMap(BiMap)- 不可变包装
七、Multisets:多重集操作
标准 Collection 操作忽略元素计数,而 Multisets 提供考虑元素重数的操作:
| 方法 | 说明 | 与 Collection 方法的区别 |
|---|---|---|
containsOccurrences | 检查超集是否包含子集的所有出现次数 | containsAll 只检查元素是否存在 |
removeOccurrences | 按出现次数移除元素 | removeAll 移除所有匹配元素的实例 |
retainOccurrences | 保留不超过指定次数的元素 | retainAll 保留所有匹配元素的实例 |
intersection | 返回多重集交集视图 | 无对应方法 |
Multiset<String> multiset1 = HashMultiset.create();
multiset1.add("a", 2);
Multiset<String> multiset2 = HashMultiset.create();
multiset2.add("a", 5);
multiset1.containsAll(multiset2); // true(只检查元素存在)
Multisets.containsOccurrences(multiset1, multiset2); // false(检查计数)
Multisets.removeOccurrences(multiset2, multiset1);
// multiset2 现在包含 3 个 "a"
其他实用方法:
copyHighestCountFirst(Multiset)- 按频率降序返回不可变副本unmodifiableMultiset(Multiset)- 不可变视图
八、Multimaps:多重映射
8.1 index:非唯一索引
当需要根据非唯一属性分组对象时:
ImmutableSet<String> digits = ImmutableSet.of("zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine");
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
};
ImmutableListMultimap<Integer, String> digitsByLength = Multimaps.index(digits, lengthFunction);
// 结果:
// 3 => {"one", "two", "six"}
// 4 => {"zero", "four", "five", "nine"}
// 5 => {"three", "seven", "eight"}
8.2 invertFrom:反转多重映射
ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.putAll("b", Ints.asList(2, 4, 6));
multimap.putAll("a", Ints.asList(4, 2, 1));
TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(
multimap,
TreeMultimap.<Integer, String>create()
);
// 结果按顺序:
// 1 => {"a"}
// 2 => {"a", "b", "c"}
// 3 => {"c"}
// ...
8.3 forMap:Map 转 Multimap
将普通 Map 视为 SetMultimap 使用:
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2);
SetMultimap<String, Integer> multimap = Multimaps.forMap(map);
// multimap: ["a" => {1}, "b" => {1}, "c" => {2}]
Multimap<Integer, String> inverse = Multimaps.invertFrom(
multimap,
HashMultimap.<Integer, String>create()
);
// inverse: [1 => {"a", "b"}, 2 => {"c"}]
8.4 自定义 Multimap 实现
ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap(
Maps.<String, Collection<Integer>>newTreeMap(),
new Supplier<LinkedList<Integer>>() {
public LinkedList<Integer> get() {
return Lists.newLinkedList();
}
}
);
注意事项:
- Multimap 完全拥有传入的 Map 和工厂生成的集合
- 不提供线程安全保证(需使用同步包装)
get(key)返回的集合类型可能与 Supplier 返回的类型不同
九、Tables:表格工具
9.1 customTable:自定义表格实现
// 使用 LinkedHashMap 代替 HashMap
Table<String, Character, Integer> table = Tables.newCustomTable(
Maps.<String, Map<Character, Integer>>newLinkedHashMap(),
new Supplier<Map<Character, Integer>>() {
public Map<Character, Integer> get() {
return Maps.newLinkedHashMap();
}
}
);
9.2 transpose:转置表格
Table<C, R, V> transposed = Tables.transpose(originalTable);
// 将 Table<R, C, V> 转换为 Table<C, R, V>
9.3 包装器
unmodifiableTable(Table)- 不可变表格视图unmodifiableRowSortedTable(RowSortedTable)- 不可变行排序表格
建议:大多数情况下优先使用
ImmutableTable
最佳实践总结
- 优先使用不可变集合:除非确实需要可变性,否则使用 Guava 的不可变集合类
- 利用惰性求值:
Iterables和视图操作避免不必要的内存分配 - 选择合适的数据结构:根据场景选择 Multiset、Multimap 或 Table
- 静态导入工具方法:简化代码,如
static import com.google.common.collect.Lists.* - 注意线程安全:Guava 集合默认不是线程安全的,需要时使用同步包装
- 理解视图与副本:区分返回视图的操作和创建副本的操作
总结
Guava 的集合工具类极大地扩展了 JDK 集合框架的功能,提供了更丰富、更便捷的 API。通过合理使用这些工具类,您可以:
- 减少样板代码
- 提高代码可读性
- 优化性能(通过惰性求值和预分配)
- 处理复杂的数据结构需求(如多重集、多重映射、表格)
3847

被折叠的 条评论
为什么被折叠?



