本文并没有深挖 Collector 在jdk源码中的整个流程,只是介绍如何构造 Collector
Collector在 Stream 中的使用
Collector 用作 Stream类的归约操作 collect
方法的参数;collect()
有着两个方法签名,如下:
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
// 收集器Collector 对上面的三个参数进行了封装,但并不仅仅是这样
<R, A> R collect(Collector<? super T, A, R> collector);
- supplier 用于提供返回的结果容器(指可变容器);
- accumulator 用于如何将值放入一个结果容器中;
- combiner 用于并行情况下如何合并两个结果容器;
验证代码如下:
// 测试 第三个参数 combiner 是否只在并行流情况下执行
IntStream range = IntStream.range(0, 10);
range.collect(ArrayList::new, List::add, (list1, list2) -> {
System.out.println(Thread.currentThread().getName()+"执行了");
list1.addAll(list2);
});
System.out.println("并行流:");
AtomicInteger count = new AtomicInteger(1);
IntStream.range(0, 10).parallel().collect(ArrayList::new, List::add, (list1, list2) -> {
System.out.println(Thread.currentThread().getName()+"执行了"+count.getAndDecrement());
list1.addAll(list2);
});
System.out.println("count: "+ count.get());
Collector 接口
collector 接口提供五个抽象方法,分别返回了 supplier、accumulator、combiner、finisher和characteristics。
前三者的和之前介绍的一样,不过combiner 是有区别的,这里的 combiner 是一个 BinaryOperator,也就是说它除了指定如何在并发环境下如何操作两个结果容器,还允许指定返回的结果容器;finisher 是对结果容器最后的转换;characteristics 是 collector 的属性,能够优化归约操作;结合下面代码理解:
Stream<String> stream1 = Stream.of("I", "Love", "You", "!");
List<String> result = stream1.collect((Supplier<List<String>>)ArrayList::new, List::add, (list1, list2) -> list1.addAll(list2));
result.forEach(System.out::println);
System.out.println("使用Collectors:");
Stream<String> stream2 = Stream.of("I", "Love", "You", "!");
List<Integer> result2 = stream2.collect(new Collector<String, List<String>, List<Integer>>() {
@Override
public Supplier<List<String>> supplier() {
return ()->{
System.out.println("产生容器:list");
return new ArrayList<>();
};
}
@Override
public BiConsumer<List<String>, String> accumulator() {
return (list,s)->{
System.out.println("往容器中添加元素");
list.add(s);
};
}
@Override
public BinaryOperator<List<String>> combiner() {
return (list1, list2) -> {
System.out.println("合并结果集");
list1.addAll(list2);
return list1;
};
}
@Override
public Function<List<String>, List<Integer>> finisher() {
return r1->{
System.out.println("我将追加后缀:hha");
List<Integer> r2 = new ArrayList<>();
r2.add(r1.size());
return r2;
};
}
/**
* 如果在这里指定了 characteristics 集包含了 IDENTITY_FINISH,那么将不会执行 finisher方法
* 由于泛型的原因,仍然能够返回 List<Integer> ,最终打印的时候会报类型转换错误
* @author duofei
* @date 2019/7/27
* @return void
*/
@Override
public Set<Characteristics> characteristics() {
//return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
return new HashSet<>();
}
});
result2.forEach(System.out::println);
总结
使用 Collector 进行归约操作,需要指定返回的结果容器 ( supplier),怎样往结果容器中添加元素(accumulator),以及如何在并发情况下,合并结果容器(combiner),除此之外,还提供了对结果容器进行再处理的方法(finisher),除此之外,还需要指定属性(characteristics),来优化归约操作。
Collectors 类提供了一系列 Collector,例如:
// 具体参考源码,和上述的构建一样
public static <T>
Collector<T, ?, Set<T>> toSet() {
return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
(left, right) -> { left.addAll(right); return left; },
CH_UNORDERED_ID);
}
// 如果我们自己写,可能是这样
BinaryOperator<Set<T>> combiner = (Set<T> left, Set<T> right) -> {
left.addAll(right);
return left;
};
Collector<T, ?, Set<T>> toSet = Collector.of(HashSet::new, Set::add,combiner,
Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH);
现在,除了使用 Collectors 提供的Collector 以外,可以自己写更符合使用场景的 Collector了。
如果你觉得我的文章对你有所帮助,欢迎关注我的公众号。赞!
认认真真学习,做思想的产出者,而不是文字的搬运工。错误之处,还望指出!