创建自定义的Collector
理解Collector
是如何工作的
Collector
只能在对象Stream.collect()
,如果想要在数字stream中收集元素,你需要理解Collector
接口。
简单的来说,Collector
有4个基本组件构成。前2个组件用于收集stream流中的元素。第3个组件仅在并行stream流中需要。某些类型的collector需要第4个组件来对构建的容器做后置处理。
第一个组件用于提供收集stream流中元素所放置的容器,例如ArrayList
, HashSet
, HashMap
等。
Creating such a container can be modeled with an instance of
Supplier
. This first component is called the supplier.
第二个组件用于添加stream流中元素到容器中。这个组件会被反复调用,知道stream流中的元素一个一个全部添加到容器中。
在Collector API中,这个组件是BiConsumer
的实例。有2个参数:
-
第一个参数为容器(第一个组件所创建的)
-
第二个参数为stream流中的元素
This biconsumer is called the accumulator in the context of the Collector API.
这2个组件已经足以让Collector
工作,但Stream API中有一个约束,使得需要更多的组件才能工作。
由于Stream API支持并行操作。 Collector API可以工作在这样的上下文:各个子stream流只能被自己Collector
创建的容器所收集。
一旦所有的子stream流处理完成,就会产生数个容器(子stream流所创建)。这些容器完全相同,因为它们都是由一个组件所创建(第一个组件的Supplier
)。现在你需要合并这些容器到一个容器中。因此Collector API需要第三个组件combiner,合并这些容器。
A combiner is modeled by an instance of
BinaryOperator
that takes two partially filled containers and returns one.
This
BinaryOperator
is also modeled by aBiConsumer
in thecollect()
overloads of the Stream API.
第四个组件
The fourth component is called the finisher, and will be covered later in this part.
收集原始数据类型到Collection
中
特殊的数字stream流的collect()
,只要前三个组件。
IntStream.collect()
的三个参数:
-
Supplier
实例,叫supplier -
ObjIntConsumer
实例,叫accumulator -
BiConsumer
实例,叫combiner
example
Supplier<List<Integer>> supplier = ArrayList::new;
ObjIntConsumer<List<Integer>> accumulator = Collection::add;
BiConsumer<List<Integer>, List<Integer>> combiner = Collection::addAll;
List<Integer> collect =
IntStream.range(0, 10)
.collect(supplier, accumulator, combiner );
System.out.println("collect = " + collect);
result
collect = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
收集原始数据类型到StringBuffer
example
Supplier<StringBuffer> supplier = StringBuffer::new;
ObjIntConsumer<StringBuffer> accumulator = StringBuffer::append;
BiConsumer<StringBuffer, StringBuffer> combiner = StringBuffer::append;
StringBuffer collect =
IntStream.range(0, 10)
.collect(supplier, accumulator, combiner);
System.out.println("collect = " + collect);
result
collect = 0123456789
使用Collector
的后置处理finisher
Collectors.collectintAndThen()
example1
Collection<String> strings =
List.of("two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve");
Map<Integer, Long> histogram =
strings.stream()
.collect(
Collectors.groupingBy(
String::length,
Collectors.counting()));
Map.Entry<Integer, Long> maxValue =
histogram.entrySet().stream()
.max(Map.Entry.comparingByValue())
.orElseThrow();
System.out.println("maxValue = " + maxValue);
example2
Collection<String> strings =
List.of("two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve");
Function<Map<Integer, Long>, Map.Entry<Integer, Long>> finisher =
map -> map.entrySet().stream()
.max(Map.Entry.comparingByValue())
.orElseThrow();
Map.Entry<Integer, Long> maxValue =
strings.stream()
.collect(
Collectors.collectingAndThen(
Collectors.groupingBy(
String::length,
Collectors.counting()),
finisher
));
System.out.println("maxValue = " + maxValue);
使用Collectors.teeing()
组合俩个Collector
结果
jdk12新增Collectors.teeing()
假设你有下面记录
enum Color {
RED, BLUE, WHITE, YELLOW
}
enum Engine {
ELECTRIC, HYBRID, GAS
}
enum Drive {
WD2, WD4
}
interface Vehicle {}
record Car(Color color, Engine engine, Drive drive, int passengers) implements Vehicle {}
record Truck(Engine engine, Drive drive, int weight) implements Vehicle {}
假设在车辆集合中找出所有电车和电卡车
List<Vehicle> electricVehicles = vehicles.stream()
.collect(
Collectors.teeing(
Collectors.filtering(
vehicle -> vehicle instanceof Car car && car.engine() == Engine.ELECTRIC,
Collectors.toList()),
Collectors.filtering(
vehicle -> vehicle instanceof Truck truck && truck.engine() == Engine.ELECTRIC,
Collectors.toList()),
(cars, trucks) -> {
cars.addAll(trucks);
return cars;
}));