Java 8 流 - 合并共享相同 Id 的对象集合

2022-09-03 09:21:10

我有一张发票:

class Invoice {
  int month;
  BigDecimal amount
}

我想合并这些发票,因此我每月收到一张发票,该金额是本月发票金额的总和。

例如:

invoice 1 : {month:1,amount:1000}
invoice 2 : {month:1,amount:300}
invoice 3 : {month:2,amount:2000}

输出:

invoice 1 : {month:1,amount:1300}
invoice 2 : {month:2,amount:2000}

如何使用 java 8 流执行此操作?

编辑:由于我的发票类是可变的,修改它们不是问题,我选择了Eugene的解决方案

Collection<Invoice>  invoices = list.collect(Collectors.toMap(Invoice::getMonth, Function.identity(), (left, right) -> {
                left.setAmount(left.getAmount().add(right.getAmount()));
                return left;
            })).values();

答案 1

如果您可以返回,它将如下所示:Collection

Collection<Invoice>  invoices = list.collect(Collectors.toMap(Invoice::getMonth, Function.identity(), (left, right) -> {
                left.setAmount(left.getAmount().add(right.getAmount()));
                return left;
            })).values();

如果你真的需要一个 :List

 list.stream().collect(Collectors.collectingAndThen(Collectors.toMap(Invoice::getMonth, Function.identity(), (left, right) -> {
                left.setAmount(left.getAmount().add(right.getAmount()));
                return left;
            }), m -> new ArrayList<>(m.values())));

两者显然都认为这是可变的...Invoice


答案 2

如果可以将以下复制构造函数和合并方法添加到类中:Invoice

public Invoice(Invoice another) {
    this.month = another.month;
    this.amount = another.amount;
}

public Invoice merge(Invoice another) {
    amount = amount.add(another.amount); // BigDecimal is immutable
    return this;
}

您可以根据需要减少,如下所示:

Collection<Invoice> result = list.stream()
    .collect(Collectors.toMap(
        Invoice::getMonth, // use month as key
        Invoice::new,      // use copy constructor => don't mutate original invoices
        Invoice::merge))   // merge invoices with same month
    .values();

我正在使用 Collectors.toMap 来完成这项工作,它有三个参数:一个将流的元素映射到键的函数,一个将流的元素映射到值的函数,以及一个用于在键上发生冲突时合并值的合并函数。


推荐