我有一系列发票:
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流做到这一点?
编辑:由于我的发票类是可变的,修改它们不是问题,我选择了尤金的解决方案Collection<Invoice> invoices = list.collect(Collectors.toMap(Invoice::getMonth, Function.identity(), (left, right) -> {
left.setAmount(left.getAmount().add(right.getAmount()));
return left;
})).values();
答案 0 :(得分:17)
如果你可以返回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
是可变的......
答案 1 :(得分:6)
如果您可以将以下复制构造函数和合并方法添加到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
来完成这项工作,它有三个参数:一个将流的元素映射到键的函数,一个将流的元素映射到值的函数和一个合并函数用于在键上发生碰撞时组合值。
答案 2 :(得分:1)
Collection<Invoice> result = invoices.stream().collect(groupingBy(i -> i.month,
collectingAndThen(
reducing((Invoice i1, Invoice i2) -> new Invoice(i1.month, i1.amount + i2.amount)),
Optional::get))).values();
答案 3 :(得分:0)
您可以执行类似
的操作 Map<Integer, Invoice> invoiceMap = invoices.stream()
.collect(Collectors.groupingBy( // group invoices by month
invoice -> invoice.month
))
.entrySet().stream() // once you have them grouped stream then again so...
.collect(Collectors.toMap(
entry -> entry.getKey(), // we can mantain the key (month)
entry -> entry.getValue().stream() // and streaming all month's invoices
.reduce((invoice, invoice2) -> // add all the ammounts
new Invoice(invoice.month, invoice.amount.add(invoice2.amount)))
.orElse(new Invoice(entry.getKey(), new BigDecimal(0))) // In case we don't have any invoice (unlikeable)
));
答案 4 :(得分:0)
以下是我的图书馆的解决方案:AbacusUtil
Stream.of(invoices)
.groupBy2(Invoice::getMonth, Invoice::getAmount, BigDecimal::add)
.map(e -> new Invoice(e.getKey(), e.getValue())) // Probably we should not modify original invoices. create new instances.
.toList();
答案 5 :(得分:0)
我认为,如果您的应用程序不支持lambda,那么这可能是一个合适的答案,例如(Android minSdkVersion = 16不支持lambda)
public static List<Invoice> mergeAmount(List<Invoice> invoiceList) {
List<Invoice> newInvoiceList = new ArrayList<>();
for(Invoice inv: invoiceList) {
boolean isThere = false;
for (Invoice inv1: newInvoiceList) {
if (inv1.getAmount() == inv.getAmount()) {
inv1.setAmount(inv1.getAmoount()+inv.getAmount());
isThere = true;
break;
}
}
if (!isThere) {
newInvoiceList.add(inv);
}
}
return newInvoiceList;
}