前言:在做报表统计的时候会经常对数据进行分组,然后相加。这里我提出四种情况
先写测试类
@Data
public class ss {
private BigDecimal age;
private BigDecimal money;
private String name;
public ss(BigDecimal age, BigDecimal money, String name) {
this.age = age;
this.money = money;
this.name = name;
}
}
工具类:
public class BigDecimalFunctionUtil {
public static <T> Collector<T, ?, BigDecimal> summingBigDecimal(ToBigDecimalFunction<? super T> mapper) {
return new CollectorImpl<>(() -> new BigDecimal[1], (a, t) -> {
if (a[0] == null) {
a[0] = BigDecimal.ZERO;
}
a[0] = a[0].add(mapper.applyAsBigDecimal(t));
}, (a, b) -> {
a[0] = a[0].add(b[0]);
return a;
}, a -> a[0], CH_NOID);
}
}
public 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;
}
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
}
@FunctionalInterface
public interface ToBigDecimalFunction<T> {
BigDecimal applyAsBigDecimal(T value);
}
一、根据一个name字段分组,只有一个age字段相加,得到age的总和
Map<String, BigDecimal> collect = s.stream().collect(Collectors.groupingBy(ss::getName, util.summingBigDecimal(ss::getAge)));
BigDecimal totalAge = collect1.get("b");
二、根据一个name字段分组,money和age字段相加,分别得到money和age的总和
Map<String, List<ss>> collect = s.stream().collect(Collectors.groupingBy(ss::getName));
List<ss> b = collect.get("b");
BigDecimal collect1 = b.stream().collect(util.summingBigDecimal(ss::getAge)); // age的总和
BigDecimal collect2 = b.stream().collect(util.summingBigDecimal(ss::getMoney)); // money的总和
三、根据name和age字段分组,只有一个money字段相加,得到money的总和
* Map<AbstractMap.SimpleEntry<String, BigDecimal>, BigDecimal> collect = s.stream().collect(Collectors.groupingBy(record -> new AbstractMap.SimpleEntry<>(record.getName(), record.getAge()), util.summingBigDecimal(ss::getMoney)));
// 输出结果
collect.forEach((key, totalValue) ->
{
System.out.println("name: " + key.getKey() + ", age: " + key.getValue() + ", Total Money: " + totalValue);
}
);
三、根据name和age字段分组,money和age字段相加,分别得到money和age的总和
Map<AbstractMap.SimpleEntry<String, BigDecimal>, List<ss>> collect = s.stream().collect(Collectors.groupingBy(record -> new AbstractMap.SimpleEntry<>(record.getName(), record.getAge())));
// 输出结果
collect.forEach((key, totalValue) ->
{
BigDecimal totalMoney = totalValue.stream().collect(util.summingBigDecimal(ss::getMoney));
System.out.println("name: " + key.getKey() + ", age: " + key.getValue() + ", Total Money: " + totalMoney);
}
);
总结:其实就是几个条件分组和几个结果相加。使用到的stream流返回的结果不一样,一般不会只有一个结果列相加,会有很多,所以推荐使用先分组,再分别取每一组内的数据元素的和