深入解析 Guava Collection 工具类:提升 Java 集合操作效率

深入解析 Guava Collection 工具类:提升 Java 集合操作效率

引言

任何熟悉 JDK 集合框架的程序员都深知 java.util.Collections 工具类的价值。Google Guava 在此基础上提供了更多强大的静态工具方法,适用于所有集合类型。这些工具类是 Guava 库中最成熟、最受欢迎的部分之一。本文将深入探讨 Guava 提供的各类集合工具,包括 Lists, Sets, Maps, Multisets, Multimaps, Tables 以及 Iterables 等,帮助您编写更简洁、高效的 Java 代码。

核心工具类概览

Guava 按照集合接口类型将工具方法分组到对应的工具类中:

接口类型来源Guava 工具类
CollectionJDKCollections2
ListJDKLists
SetJDKSets
MapJDKMaps
QueueJDKQueues
MultisetGuavaMultisets
MultimapGuavaMultimaps
BiMapGuavaMaps
TableGuavaTables

注意: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 提供多种 ArrayListLinkedList 的创建方法:

  • 基础创建: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))
自定义 ComparatorComparators.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

最佳实践总结

  1. 优先使用不可变集合:除非确实需要可变性,否则使用 Guava 的不可变集合类
  2. 利用惰性求值Iterables 和视图操作避免不必要的内存分配
  3. 选择合适的数据结构:根据场景选择 Multiset、Multimap 或 Table
  4. 静态导入工具方法:简化代码,如 static import com.google.common.collect.Lists.*
  5. 注意线程安全:Guava 集合默认不是线程安全的,需要时使用同步包装
  6. 理解视图与副本:区分返回视图的操作和创建副本的操作

总结

Guava 的集合工具类极大地扩展了 JDK 集合框架的功能,提供了更丰富、更便捷的 API。通过合理使用这些工具类,您可以:

  • 减少样板代码
  • 提高代码可读性
  • 优化性能(通过惰性求值和预分配)
  • 处理复杂的数据结构需求(如多重集、多重映射、表格)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值