前言
今天想用下jdk1.8的新特性,对记账流水进行按照账户id进行统计汇总金额,查找jdk1.8的新特性发现没有实现对BigDecimal的求和方法。一开始用下面的方式搞定的,比较土鳖。另外还有个需求,更新账户余额的时候,为了避免死锁,要按照账户号顺序进行更新,这就涉及到汇总后按账户号排序的问题,这里用的是TreeMap。
第一版实现
TreeMap<Long, List<AccountingDetailVo>> resultList222 = voList.stream().collect(
Collectors.groupingBy(AccountingDetailVo::getAcctNo,TreeMap::new,Collectors.toList()));
TreeMap<Long, BigDecimal> acctMap = new TreeMap<>();
for (Entry<Long, List<AccountingDetailVo>> entry:resultList222.entrySet()) {
acctMap.put(entry.getKey(),
entry.getValue().stream().map(AccountingDetailVo::getAmtByBalDir).reduce(BigDecimal.ZERO, BigDecimal::add));
}
后来从网上查了查,可以自定义实现,参考文章我给关了,这里就不引用了,具体实现如下:
自定义个Function
@FunctionalInterface
public interface ToBigDecimalFunction <T> {
BigDecimal applyAsBigDecimal(T value);
}
自定义个CollectorsUtils
public class CollectorsUtils {
static final Set<Characteristics> CH_NOID = Collections.emptySet();
private CollectorsUtils() {
}
@SuppressWarnings("unchecked")
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
//自定义个Collector实现类
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;
}
}
// 创建个汇总的方法
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);
}
}
要计算的bean
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AccountingDetailVo implements Serializable{
private static final long serialVersionUID = 6226733035919673733L;
@ApiModelProperty(value = "资金类型")
private Long amtType;
@ApiModelProperty(value = "账户")
private Long acctNo;
@ApiModelProperty(value = "账户类型")
private Long acctType;
@ApiModelProperty(value = "客户")
private Long custNo;
@ApiModelProperty(value = "发生方向")
private String balDir;
@ApiModelProperty(value = "金额")
private BigDecimal amt;
/**
* 根据资金方向取得金额
*/
public double getAmtByBalDirToDouble() {
return this.getBalDir().equals(BalDirEnum.ADD.getValue()) ? this.getAmt().doubleValue() : this.getAmt().negate().doubleValue();
}
public BigDecimal getAmtByBalDir() {
return this.getBalDir().equals(BalDirEnum.ADD.getValue()) ? this.getAmt() : this.getAmt().negate();
}
}
测试用例
public class ListGroupTest2 {
public static void main(String[] args) {
List<AccountingDetailVo> voList = new ArrayList<>();
AccountingDetailVo vo1 = AccountingDetailVo.builder().acctNo(1L).balDir("-").amt(BigDecimal.valueOf(500.22)).build();
AccountingDetailVo vo2 = AccountingDetailVo.builder().acctNo(2L).balDir("+").amt(BigDecimal.valueOf(500.33)).build();
AccountingDetailVo vo3 = AccountingDetailVo.builder().acctNo(1L).balDir("-").amt(BigDecimal.valueOf(500.44)).build();
AccountingDetailVo vo4 = AccountingDetailVo.builder().acctNo(2L).balDir("+").amt(BigDecimal.valueOf(500.55)).build();
AccountingDetailVo vo5 = AccountingDetailVo.builder().acctNo(3L).balDir("+").amt(BigDecimal.valueOf(500.66)).build();
voList.add(vo1);
voList.add(vo2);
voList.add(vo3);
voList.add(vo4);
voList.add(vo5);
TreeMap<Long, BigDecimal> resultList333 = voList.stream().collect(
Collectors.groupingBy(AccountingDetailVo::getAcctNo,TreeMap::new, CollectorsUtils.summingBigDecimal(AccountingDetailVo::getAmtByBalDir)));
System.out.println(JSON.toJSONString(resultList333, SerializerFeature.PrettyFormat));
}
}
如果是多个字段要进行group by呢?
可以看下groupingBy的参数信息,第一个传入的是Function,可以在要计算的bean里面增加个get方法就好,把要group by的字段拼串吧。
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)