1. 引言

本文是 Google Guava 库第 21 版新特性系列文章的第一篇。我们将讨论新增的类以及对之前版本的重大变更。

具体来说,我们会聚焦 common.collect 包中的新增功能和改进。

Guava 21 在 common.collect 包中引入了一些实用的新工具,下面快速了解这些工具以及如何充分利用它们。

2. Streams 类

Java 8 引入的 java.util.stream.Stream 让我们都很兴奋。现在 Guava 充分利用了流式 API,补充了标准库缺失的功能。

Streams 是一个静态工具类,提供了处理 Java 8 流的实用方法。

2.1 Streams.stream()

Streams 类提供了四种方式从 IterableIteratorOptionalCollection 创建流。

⚠️ 注意:通过 Collection 创建流的方法已被弃用,因为 Java 8 已原生支持:

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Stream<Integer> streamFromCollection = Streams.stream(numbers);
Stream<Integer> streamFromIterator = Streams.stream(numbers.iterator());
Stream<Integer> streamFromIterable = Streams.stream((Iterable<Integer>) numbers);
Stream<Integer> streamFromOptional = Streams.stream(Optional.of(1));

Streams 类还提供了 OptionalDoubleOptionalLongOptionalInt 的重载版本。这些方法返回仅包含该元素的流(如果存在),否则返回空流:

LongStream streamFromOptionalLong = Streams.stream(OptionalLong.of(1));
IntStream streamFromOptionalInt = Streams.stream(OptionalInt.of(1));
DoubleStream streamFromOptionalDouble = Streams.stream(OptionalDouble.of(1.0));

2.2 Streams.concat()

Streams 类提供了合并多个同类型流的方法:

Stream<Integer> concatenatedStreams = Streams.concat(streamFromCollection, streamFromIterable,streamFromIterator);

concat 方法还支持 LongStreamIntStreamDoubleStream 的重载版本。

2.3 Streams.findLast()

通过 findLast() 方法可以快速获取流中的最后一个元素:

List<Integer> integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Optional<Integer> lastItem = Streams.findLast(integers.stream());

如果流为空,返回 Optional.empty()。该方法同样适用于 LongStreamIntStreamDoubleStream

2.4 Streams.mapWithIndex()

使用 mapWithIndex() 方法时,流中的每个元素都会携带其位置(索引)信息:

mapWithIndex( Stream.of("a", "b", "c"), (str, index) -> str + ":" + index)

返回结果为 Stream.of("a:0","b:1","c:2")

通过重载的 mapWithIndex() 方法,IntStreamLongStreamDoubleStream 也能实现相同功能。

2.5 Streams.zip()

使用 zip 方法可以将两个流中的对应元素通过指定函数映射:

Streams.zip(
  Stream.of("candy", "chocolate", "bar"),
  Stream.of("$1", "$2","$3"),
  (arg1, arg2) -> arg1 + ":" + arg2
);

返回结果为 Stream.of("candy:$1","chocolate:$2","bar:$3")

✅ 结果流的长度等于两个输入流中较短的那个,较长流的剩余元素会被忽略。

3. Comparators 类

Guava 的 Ordering 类已被弃用,并将在新版本中移除。其大部分功能已被 JDK 8 的比较器替代。

Guava 新增的 Comparators 类提供了 Java 8 标准库尚未支持的 Ordering 高级功能。

3.1 Comparators.isInOrder()

该方法检查可迭代对象中的每个元素是否按 Comparator 指定的顺序大于或等于前一个元素:

List<Integer> integers = Arrays.asList(1,2,3,4,4,6,7,8,9,10);
boolean isInAscendingOrder = Comparators.isInOrder(
  integers, new AscendingOrderComparator());

3.2 Comparators.isInStrictOrder()

isInOrder() 类似,但要求更严格:元素必须严格大于前一个元素(不能相等)。上面示例代码对此方法会返回 false

3.3 Comparators.lexicographical()

该 API 返回一个新的 Comparator 实例,按字典序(lexicographical)成对比较对应元素。内部实际创建了 LexicographicalOrdering<S>() 的实例。

4. MoreCollectors 类

MoreCollectors 包含一些 Java 8 java.util.stream.Collectors 未提供的实用收集器,且与 com.google.common 类型无关。

4.1 MoreCollectors.toOptional()

该收集器将包含零个或一个元素的流转换为 Optional

List<Integer> numbers = Arrays.asList(1);
Optional<Integer> number = numbers.stream()
  .map(e -> e * 2)
  .collect(MoreCollectors.toOptional());

❌ 如果流包含多个元素,会抛出 IllegalArgumentException

4.2 MoreCollectors.onlyElement()

该收集器从仅包含一个元素的流中提取该元素:

  • 如果流包含多个元素 → 抛出 IllegalArgumentException
  • 如果流为空 → 抛出 NoSuchElementException

5. Interners.InternerBuilder

这是 Guava 现有 Interners 的内部构建器类。它提供便捷方法定义 Interner 的并发级别和类型(弱引用或强引用):

Interners interners = Interners.newBuilder()
  .concurrencyLevel(2)
  .weak()
  .build();

6. 总结

本文快速介绍了 Guava 21 中 common.collect 包的新增功能。这些工具类弥补了 Java 8 标准库的不足,提供了更简洁高效的流处理、比较和收集操作。

完整代码示例可在 GitHub 获取。


原始标题:New Stream, Comparator and Collector in Guava 21