0%

stream and lambda(17) - 终止操作之 stream 数据收集 collect

Stream 虽然已经提供了不少终止操作,但是可能在有些场景下还是无法满足你的使用要求。没关系,如果你发现你想要的终止操作功能接口 Stream 没有提供,不妨试试从 collect 方法查找一下,很大概率上你会有意外收获。

方法定义

<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);

<R, A> R collect(Collector<? super T, A, R> collector);

collect 提供了两个方法,一个是三参数方法,单看参数的话,其实和 reduce 特别像。单参数的方法,需要传入一个收集器。其实点进收集器内部不难发现,Collector 内部定义了三参数方法需要的所有内容。二者的作用都是将流内元素按照一定的要求汇聚成一个最终结果。

1
2
3
4
5
6
7
8
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();
//...省略部分内容
}

collect 方法的返回类型来看,collect 方法的主要作用是将流所有元素汇聚成一个值。从某种意义讲,其实也是 reduce

三参数 collect

从参数来看,三参数的 collect 很像 reduce,其实从实现来看,也有些像。

1
2
3
4
5
6
7
8
9
10
11
public final <R> R reduce(R identity,
BiFunction<R, ? super P_OUT, R> accumulator,
BinaryOperator<R> combiner) {
return evaluate(ReduceOps.makeRef(identity, accumulator, combiner));
}

public final <R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super P_OUT> accumulator,
BiConsumer<R, R> combiner) {
return evaluate(ReduceOps.makeRef(supplier, accumulator, combiner));
}

reduce 一样,在串行流下,combiner 也是不会生效参与运算的。

1
2
3
4
5
6
7
8
public void collectTest() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
ArrayList<Integer> collect = stream.collect(ArrayList::new, ArrayList::add, (l1, l2) -> {
System.out.println("this is the third parameter");
l1.addAll(l2);
});
System.out.println(collect);
}

在串行流下,collect 仍然将流合并成了一个 ArrayList,并正常输出,但是却没有出现我们主动打印的 this is the third parameter

[1, 2, 3, 4, 5]

当然,大多数情况下我们是可以不用三参数方法,因为 JDK 贴心的为我们提供各种各样的收集器。

带收集器的 collect

collect 方法可以传入一个收集器,方法会根据收集器内部的定义,来自动完成我们想要的功能。

比如,我们想要将流内元素转成一个 List,可以用下面的方式来实现。

1
2
3
4
5
public void collectTest2() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
List<Integer> collect = stream.collect(Collectors.toList());
System.out.println(collect);
}

方法最终的结果,和我们自己用三参数的方式写的代码输出的结果,是一致的。

对于常用的收集器,JDK 专门提供了一个 Collectors 类,定义了我们常用的不常用的收集器。

收集器 Collectors

转成集合

方法作用
toCollection将数据转成Collection
toList将数据转成List
toSet将数据转成Set
toMap将数据转成Map
toConcurrentMap将数据转成concurrentMap

转成值

方法作用
counting统计流元素个数
joining拼接成一个字符串
minBy根据比较器取最小值
maxBy根据比较器取最大值
reducing数据汇聚,类似 reduce
averagingIntint流求平均数
averagingLonglong流求平均数
averagingDoubledouble流求平均数
summingIntint流求总和
summarizingLonglong流求总和
summarizingDoubledouble流求总和

分组与分块

方法作用
collectingAndThen收集数据后再做一些额外操作
mapping先map,再收集
groupingBy数据分组
groupingByConcurrent并行数据分组
partitioningBy数据分成多部分

总结

  • collect 方法,我们可以自己使用三参数方法来调用,也可以直接用 JDK 提供好的收集器。
  • 收集器汇总在 java.util.stream.collectors