2017-06-14 52 views
3

的藏品我有发票的集合:Java的8个流 - 合并对象共享相同的ID

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(); 

回答

8

如果你是OK返回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是可变的...

+0

或使用'Collectors.collectingAndThen(Collectors.toMap(...),m-> new ArrayList <>(m.values()))'... – Holger

+0

@Holger好点!编辑... – Eugene

+2

嗨! 2条评论... 1)也许这应该是'list.stream()。收集(...)'和2)您变异原'Invoice'元素,这看起来并不正确,我...你可以通过在值映射器将一个拷贝构造函数,并用它解决它 –

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) 
      )); 
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(); 
3

如果你可以添加下面的拷贝构造函数和方法合并到您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做的工作,其中有三个参数:映射在流键的元素的功能,映射功能流,以值和用于当有上的键碰撞到值进行组合合并功能的元件。