2014-07-04 42 views
5

我有下面的类:总结列表<X>列出<X>与Java 8个流API

class Money { 
    CurrencyUnit currencyUnit; 
    BigDecimal amount; 
} 

在我的申请,我得到Money对象的一些随机列表:

currencyUnit | amount 
--------------------- 
EUR   | 5.1 
EUR   | 0 
USD   | 1.09 
EUR   | 42 
USD   | 3 

现在我想使用Java 8 Stream API创建以下结果(仅针对每个货币单位的金额调用BigDecimal::add):

currencyUnit | amount 
--------------------- 
EUR   | 47.1 
USD   | 4.09 

我已经知道/ DID:

Stream<Money> moneyStream = moneyList.stream(); 

这里,它已经结束。我知道我可以使用一个Collector产生Map<CurrencyUnit, List<Money>>

moneyStream.collect(Collectors.groupingBy(m -> m.getCurrencyUnit()); 

但后来我还是要经过的所有键值对,并总结量数据。

什么是这样做的(也可能是最简单的方法)?它不可能那么复杂,对吧? :)


编辑:如果它不是那么清楚我需要什么,这是我的旧路Java代码:

Map<CurrencyUnit, Money> map = new HashMap<>(); 
moneyList.stream().forEach(e -> { 
    Money m = map.get(e.getCurrencyUnit()); 
    if(m == null) { 
     m = new Money(); 
     m.setAmount(BigDecimal.ZERO); 
     m.setCurrencyUnit(e.getCurrencyUnit()); 
     map.put(e.getCurrencyUnit(), m); 
    } 
    m.setAmount(m.getAmount().add(e.getAmount())); 
}); 
return map.values(); 

编辑2:另一种解决方案,这是不是真的优雅:

List<Money> list = inputList.stream() 
    .collect(Collectors.groupingBy(Money::getCurrencyUnit)) 
    .values().stream().map(ml -> { 
     Money money = new Money(); 
     ml.forEach(m -> { 
      if(money.getCurrencyUnit() == null) { 
       money.setCurrencyUnit(m.getCurrencyUnit()); 
       money.setAmount(m.getAmount()); 
      } else { 
       money.setAmount(money.getAmount().add(m.getAmount())); 
      } 
     }); 
     return money; 
    }).collect(Collectors.toList()); 

回答

10

您可以通过CurrencyUnit使用groupingBy收集到组的对象。没有第二个参数,groupingBy方法将元素收集到列表中。但是,如果您需要其他内容,您还可以指定下游收集器

您可以使用Collectors::summingIntCollectors::summingLongintlong。对于BigDecimal,你可以回退到Collectors::reducing

import static java.util.stream.Collectors.groupingBy; 
import static java.util.stream.Collectors.reducing; 

Map<CurrencyUnit, BigDecimal> result = moneyList.stream() 
    .collect(
     groupingBy(
      Money::getCurrencyUnit, 
      reducing(BigDecimal.ZERO, Money::getAmount, BigDecimal::add))); 

编辑:您还可以创建List<Money>

List<Money> result = moneyList.stream() 
    .collect(
     groupingBy(
      Money::getCurrencyUnit, 
      reducing(BigDecimal.ZERO, Money::getAmount, BigDecimal::add))) 
    .entrySet().stream() 
    .map(e -> new Money(e.getKey(), e.getValue()) 
    .collect(toList()); 
+0

谢谢。这与我所需要的很接近,但是你的代码给了我一个'Map'。是不是有可能找回一个'List'或者'Set'? (没有手动创建新的Money对象等) –

+0

就在5分钟前,迭代entrySet的想法出现在我的脑海中,但我想:这真的很丑,必须有更性感/优雅的东西。 '---'但我认为你给出的答案是这样做的唯一方法。 ' - '现在我想:'moneyStream.collect(Collectors.groupingBy(m - > m.getCurrencyUnit());'然后用map生成'Map >'。 values()。stream()'产生一个'List '?! –