起因
业务中需要将一组数据分类后收集总和,原本可以使用Collectors.summingInt()
,但是我们的数据源是BigDecimal类型的,而Java8原生只提供了summingInt、summingLong、summingDouble三种基础类型的方法。于是就自己动手丰衣足食吧。。
期望目标:
Map<String, BigDecimal> result = Arrays.stream(records).parallel().collect(Collectors.groupingBy(
Record::getType, CollectorsUtil.summingBigDecimal(Record::getAmount)));
实践
1. 依葫芦
先分析一下Collectors.summingInt()
方法
public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new int[1],
(a, t) -> { a[0] += mapper.applyAsInt(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
}
该方法接受ToIntFunction<? super T>
类型的参数,返回CollectorImpl
类型的实例化对象。CollectorImpl
是Collector
接口的唯一实现类
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
的构造器参数,可知summingInt()
方法是这样的
arg[0]创建一个计算用的容器: () -> new int[1]
arg[1]为计算逻辑: (a, t) -> { a[0] += mapper.applyAsInt(t); }
arg[2]为合并逻辑: (a, b) -> { a[0] += b[0]; return a; }
arg[3]为返回最终计算值: a -> a[0]
arg[4]为空Set(不知道干什么用。。): Collections.emptySet()
2. 画瓢
很自然的,BigDecimal的就是这样了
public static <T> Collector<T, ?, BigDecimal> summingBigDecimal(Function<? super T, BigDecimal> mapper) {
return new CollectorImpl<>(() -> new BigDecimal[] { BigDecimal.ZERO }, (a, t) -> {
a[0] = a[0].add(mapper.apply(t));
}, (a, b) -> {
a[0] = a[0].add(b[0]);
return a;
}, a -> a[0], CH_NOID);
}
Java8并行流的一些tips
- 千万注意共享变量的使用
- 注意装箱拆箱的开销
- 基于数据量考虑使用并行流本身的成本
- 谨慎在并行流中使用事务
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。