【问题标题】:Aggregate List<X> to List<X> with Java 8 Stream API使用 Java 8 Stream API 将 List<X> 聚合到 List<X>
【发布时间】:2014-07-04 15:21:40
【问题描述】:

我有以下课程:

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 创建以下结果(只需为每个 currencyUnit 的金额调用 BigDecimal::add):

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

我已经知道/做过的事情:

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

到这里就结束了。我知道我可以使用Collector 生成Map&lt;CurrencyUnit, List&lt;Money&gt;&gt;

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

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

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

    您可以将Collectors::summingIntCollectors::summingLong 用于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)));
    

    编辑:你也可以创建List&lt;Money&gt;

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

    【讨论】:

    • 谢谢。这接近我需要的,但你的代码给了我一个Map。难道不能取回ListSet 吗? (无需手动创建新的Money 对象等)
    • 就在 5 分钟前,我想到了迭代 entrySet 的想法,但我想:这真的很难看,一定有更性感/优雅的东西。 --- 但我认为你给出的答案是唯一的方法。 --- 刚才我在想:moneyStream.collect(Collectors.groupingBy(m -&gt; m.getCurrencyUnit()); 然后使用生成的Map&lt;CurrencyUnit, List&lt;Money&gt;&gt;map.values().stream() 产生一个List&lt;Money&gt; ?!
    猜你喜欢
    • 2018-01-31
    • 2021-09-18
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多