起因
业务中需要将一组数据分类后收集总和,原本可以使用Collectors.summingInt(),但是我们的数据源是BigDecimal类型的,而Java8原生只提供了summingInt、summingLong、summingDouble三种基础类型的方法。于是就自己动手丰衣足食吧。。
期望目标:
Map result = Arrays.stream(records).parallel().collect(Collectors.groupingBy(
Record::getType, CollectorsUtil.summingBigDecimal(Record::getAmount)));
实践
1. 依葫芦
先分析一下Collectors.summingInt()方法
public static Collector summingInt(ToIntFunction super T> mapper) {
return new CollectorImpl<>(
() -> new int[1],
(a, t) -> { a[0] &#43;&#61; mapper.applyAsInt(t); },
(a, b) -> { a[0] &#43;&#61; b[0]; return a; },
a -> a[0], CH_NOID);
}
该方法接受ToIntFunction super T>类型的参数&#xff0c;返回CollectorImpl类型的实例化对象。CollectorImpl是Collector接口的唯一实现类
CollectorImpl(Supplier supplier,
BiConsumer accumulator,
BinaryOperator combiner,
Function finisher,
Set characteristics) {
this.supplier &#61; supplier;
this.accumulator &#61; accumulator;
this.combiner &#61; combiner;
this.finisher &#61; finisher;
this.characteristics &#61; characteristics;
}
分析CollectorImpl的构造器参数&#xff0c;可知summingInt()方法是这样的
arg[0]创建一个计算用的容器&#xff1a; () -> new int[1]
arg[1]为计算逻辑&#xff1a; (a, t) -> { a[0] &#43;&#61; mapper.applyAsInt(t); }
arg[2]为合并逻辑&#xff1a; (a, b) -> { a[0] &#43;&#61; b[0]; return a; }
arg[3]为返回最终计算值&#xff1a; a -> a[0]
arg[4]为空Set(不知道干什么用。。)&#xff1a; Collections.emptySet()
2. 画瓢
很自然的&#xff0c;BigDecimal的就是这样了
public static Collector summingBigDecimal(ToBigDecimalFunction super T> mapper) {
return new CollectorImpl<>(() -> new BigDecimal[1], (a, t) -> {
if (a[0] &#61;&#61; null) {
a[0] &#61; BigDecimal.ZERO;
}
a[0] &#61; a[0].add(mapper.applyAsBigDecimal(t));
}, (a, b) -> {
a[0] &#61; a[0].add(b[0]);
return a;
}, a -> a[0], CH_NOID);
}
Java8并行流的一些tips
千万注意共享变量的使用
注意装箱拆箱的开销
基于数据量考虑使用并行流本身的成本
谨慎在并行流中使用事务
疑问
&#64;FunctionalInterface
public interface ToBigDecimalFunction {
/**
* Applies this function to the given argument.
*
* &#64;param value
* the function argument
* &#64;return the function result
*/
BigDecimal applyAsBigDecimal(T value);
}
这里的applyAsBigDecimal如何发挥作用&#xff1f;