起因的话,新进公司,看见了一段有意思的代码。
public final class MyCollectors {
private MyCollectors() {
}
static final Set<Collector.Characteristics> CH_ID = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;
CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Function<A, R> finisher,
Set<Characteristics> characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Set<Characteristics> characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
@Override
public BiConsumer<A, T> accumulator() {
return accumulator;
}
@Override
public Supplier<A> supplier() {
return supplier;
}
@Override
public BinaryOperator<A> combiner() {
return combiner;
}
@Override
public Function<A, R> finisher() {
return finisher;
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
}
@SuppressWarnings("unchecked")
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
public static <T> Collector<T, ?, Page<T>> toPage() {
return new CollectorImpl<>((Supplier<Page<T>>) () -> new Page<T>(), Page::add, (left, right) -> {
left.addAll(right);
left.setTotal(1);
return left;
}, CH_ID);
}
}
这个,我们基本也能猜到这个代码主要用处,应该是toPage,应该是将list转换为page对象。
但是我们平常的用法的话,基本都是自己构建对象,自己塞。或者stream的map,然后我们用一个枚举类,常量类的function<list,Page>这样的来转换list到page。这里实现了一个自己的收集器,就比较新颖。
但是我有点搞不懂各个参数的具体意义。那怎么办呢?我就想到,我应该是哪里看到过博客,果然还是得瞎几把看看,这样起码知道讲的是什么,要去哪找。
深度探秘 Java 8 函数式编程(上)_mb60f8d1355165a的技术博客_51CTO博客
可以进去看看,
他这里说的是,
四要素
一个聚合器的实现,通常需要提供四要素:
一个结果容器的初始值提供器 supplier ;
一个用于将每次二元操作的中间结果与结果容器的值进行操作并重新设置结果容器的累积器 accumulator ;
一个用于对Stream元素和中间结果进行操作的二元操作符 combiner ;
一个用于对结果容器进行最终聚合的转换器 finisher(可选) 。
那我们也实习一个自己的收集器,就实现一个累加器吧。
public class FiboCollector implements Collector<Integer, List<Integer>, Integer> {
public Supplier<List<Integer>> supplier() {
return () -> {
List<Integer> result = new ArrayList<>();
return result;
};
}
@Override
public BiConsumer<List<Integer>, Integer> accumulator() {
return (res, num) -> {
if(res.size()==0){
res.add(num);
}else{
Integer integer = res.get(0);
res.set(0,integer+num);
}
};
}
@Override
public BinaryOperator<List<Integer>> combiner() {
return (left, right) -> {
System.out.println(left);
System.out.println(right);
return left;
};
}
@Override
public Function<List<Integer>, Integer> finisher() {
return x->{ return x.get(0);};
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
public static void main(String[] args) {
Integer collect = Arrays.asList(2, 2, 3, 4, 5, 6, 7, 8, 9, 10).stream().collect(new FiboCollector());
System.out.println(collect);
}
}
这里容器的话,supplier按道理应该返回intteger的,但是考虑到累加器accumulator的话,因为是一个没有返回值的,那么应该是覆盖的,但是感觉不太对,然后想起来那我用finisher最后处理的时候,返回interger不就好了。之前容器用list不就好了,于是就写出这么一段代码。但是问题又来了combiner是干嘛用的。
一个用于对Stream元素和中间结果进行操作的二元操作符 combiner ;
不太对吧,我debug看,也没有走到这段代码。
遇事不决就百度。
(66条消息) 关于java Stream中的reduce使用和为什么要有combiner_Cyberin的博客-CSDN博客
图片讲的很清楚了,就是没用并行流之前,用的combiner,如果用parallel,应该是几个元素执行完combiner后,要合并的是,你要做的操作。那么我们肯定是两个list拿第一个元素,然后加完以后,返回一个list。
public class FiboCollector implements Collector<Integer, List<Integer>, Integer> {
public Supplier<List<Integer>> supplier() {
return () -> {
List<Integer> result = new ArrayList<>();
return result;
};
}
@Override
public BiConsumer<List<Integer>, Integer> accumulator() {
return (res, num) -> {
if(res.size()==0){
res.add(num);
}else{
Integer integer = res.get(0);
res.set(0,integer+num);
}
};
}
@Override
public BinaryOperator<List<Integer>> combiner() {
return (left, right) -> {
System.out.println(left);
System.out.println(right);
int i = left.get(0) + right.get(0);
List<Integer> data =new ArrayList<>();
data.add(i);
return data;
};
}
@Override
public Function<List<Integer>, Integer> finisher() {
return x->{ return x.get(0)+;};
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
public static void main(String[] args) {
Integer collect = Arrays.asList(2, 2, 3, 4, 5, 6, 7, 8, 9, 10).stream().parallel().collect(new FiboCollector());
System.out.println(collect);
}
}
执行也可以看到,打印出sout了。
一个用于parallel 对中间结果进行操作合并操作的二元操作符 combiner ;
这样每个元素我们就知道怎么实现了,那么我们就基本知道套路了。
我们可以去看看collects。集合聚合器 toSet(), 字符串连接器 joining(),以及列表求和(summingXXX)、最大(maxBy)、最小值(minBy)等都是这个套路。
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
那么我们能用他干嘛,我这里的话,只能想到上面的list转page。算是扩展点吧。