掌握Java Stream collect()方法:10个实用示例与最佳实践
Java Stream的collect()方法对流中的元素执行可变归约操作。这是一个终止操作。
可变的减法运算是什么?
一个可变的归约操作会处理流元素,并将其累积到一个可变的结果容器中。元素处理完毕后,一个组合函数会合并所有的结果容器来创建最终结果。
Java Stream collect()方法签名可以被简单地表达为:
有两种Java Stream collect() 方法的变体。
收集器(Collector)是为供应者(supplier)、累加器(accumulator)和组合器(combiner)对象提供包装的接口。当我们使用Collectors类来提供内置的收集器实现时,第二种方法非常有用。collect()函数的三个参数是:
- 供应商:一个创建新的可变结果容器的函数。在并行执行中,这个函数可能会被多次调用,每次都必须返回一个新的值。
累加器是一个无状态函数,必须将一个元素折叠到结果容器中。
组合器是一个无状态函数,接受两个部分结果容器并合并它们,它必须与累加器函数兼容。
stream.collect()方法示例
让我们来看一下Stream.collect()方法的一些示例。
1. 字符串列表的连接
假设你想将字符串列表连接起来创建一个新的字符串。我们可以使用Stream collect()函数来执行可变归约操作,并将列表元素连接起来。
List<String> vowels = List.of("a", "e", "i", "o", "u");
// sequential stream - nothing to combine
StringBuilder result = vowels.stream().collect(StringBuilder::new, (x, y) -> x.append(y),
(a, b) -> a.append(",").append(b));
System.out.println(result.toString());
// parallel stream - combiner is combining partial results
StringBuilder result1 = vowels.parallelStream().collect(StringBuilder::new, (x, y) -> x.append(y),
(a, b) -> a.append(",").append(b));
System.out.println(result1.toString());
输出:
aeiou
a,e,i,o,u
- The supplier function is returning a new StringBuilder object in every call.
- The accumulator function is appending the list string element to the StringBuilder instance.
- The combiner function is merging the StringBuilder instances. The instances are merged with each other with a comma between them.
- In the first case, we have a sequential stream of elements. So they are processed one by one and there is only one instance of StringBuilder. There is no use of the combiner function. That’s why the output produced is “aeiou”.
- In the second case, we have a parallel stream of strings. So, the elements are processed parallelly and there are multiple instances of StringBuilder that are being merged by the combiner function. Hence, the output produced is “a,e,i,o,u”.
- If the stream source is ordered such as List, the collect() method maintains the order while processing. If the stream source is unordered such as Set, then the collect() method can produce different results in each invocation.
如果你想要连接字符串列表,我们可以使用方法引用来减少代码大小。
String result2 = vowels.parallelStream()
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
.toString();
使用Collectors类将Stream collect()到List中
收藏家类提供了许多有用的Collector接口实现。让我们来看一个例子,我们将对整数列表进行过滤,只选择偶数。Stream filter()是一个中间操作,返回一个流。因此,我们将使用collect()函数从这个流创建列表。
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
System.out.println(evenNumbers); // [2, 4, 6]
Collectors.toList() 返回一个 Collector 实现,将输入元素累积成一个新的 List。
3. 使用stream的collect()方法,将元素收集到一个集合中。
我们可以使用Collectors.toSet()将流的元素收集到一个新的Set中。
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Set<Integer> oddNumbers = numbers.parallelStream().filter(x -> x % 2 != 0).collect(Collectors.toSet());
System.out.println(oddNumbers); // [1, 3, 5]
3. 使用流的 collect() 方法将数据收集到Map中。
我们可以使用Collectors.toMap()函数将流元素收集到一个Map中。这个方法接受两个参数,用于映射键和对应的值在Map中。
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Map<Integer, String> mapOddNumbers = numbers.parallelStream().filter(x -> x % 2 != 0)
.collect(Collectors.toMap(Function.identity(), x -> String.valueOf(x)));
System.out.println(mapOddNumbers); // {1=1, 3=3, 5=5}
4. 参与收藏的人(示例)
我们可以使用Collectors.joining()方法来获取一个按遇到顺序连接输入流CharSequence元素的Collector。我们可以使用这个方法来连接一串字符串、StringBuffer或StringBuilder。
jshell> String value = Stream.of("a", "b", "c").collect(Collectors.joining());
value ==> "abc"
jshell> String valueCSV = Stream.of("a", "b", "c").collect(Collectors.joining(","));
valueCSV ==> "a,b,c"
jshell> String valueCSVLikeArray = Stream.of("a", "b", "c").collect(Collectors.joining(",", "{", "}"));
valueCSVLikeArray ==> "{a,b,c}"
jshell> String valueObject = Stream.of("1", new StringBuffer("2"), new StringBuilder("3")).collect(Collectors.joining());
valueObject ==> "123"
输出:在中文中进行原生的改述(只需要一种选项):

结论
Java Stream的collect()主要用于将流元素收集到一个集合中。它是一个终端操作。当与并行流一起使用时,它会自动处理同步问题。Collectors类提供了许多Collector实现来帮助我们。
参考资料
- Stream collect() API Doc
- Collector Interface
- Collectors API Doc