使用Java 8 Stream API将List <X>聚合到List <X>

Ben*_*n M 6 java java-8 java-stream

我有以下课程:

class Money {
  CurrencyUnit currencyUnit;
  BigDecimal amount;
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,我得到一些随机的Money对象列表:

currencyUnit | amount
---------------------
EUR          | 5.1
EUR          | 0
USD          | 1.09
EUR          | 42
USD          | 3
Run Code Online (Sandbox Code Playgroud)

现在我想使用Java 8 Stream API创建以下结果(只需调用BigDecimal::add每个currencyUnit的金额):

currencyUnit | amount
---------------------
EUR          | 47.1
USD          | 4.09
Run Code Online (Sandbox Code Playgroud)

我已经知道/做了什么:

Stream<Money> moneyStream = moneyList.stream();
Run Code Online (Sandbox Code Playgroud)

它已经结束了.我知道我可以用a Collector来制作一个Map<CurrencyUnit, List<Money>>:

moneyStream.collect(Collectors.groupingBy(m -> m.getCurrencyUnit());
Run Code Online (Sandbox Code Playgroud)

但是,我仍然必须通过所有键值对并总结数量.

什么是正确的(也可能是最简单的方法)呢?它不可能那么复杂吧?:)


编辑:如果我不清楚我需要什么,这是我的旧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();
Run Code Online (Sandbox Code Playgroud)

编辑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());
Run Code Online (Sandbox Code Playgroud)

nos*_*sid 14

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

你可以使用Collectors::summingIntCollectors::summingLongfor intlong.因为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)));
Run Code Online (Sandbox Code Playgroud)

编辑:您还可以创建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());
Run Code Online (Sandbox Code Playgroud)