【问题标题】:Java streams adding multiple values conditionallyJava 流有条件地添加多个值
【发布时间】:2021-02-27 11:29:34
【问题描述】:

我有一个这样的对象列表,其中数量可以是负数或正数:

class Sale {
   String country;
   BigDecimal amount;
}

我想以国家/地区所有负值和所有正值的总和结束。

使用这些值:

country | amount
nl      | 9
nl      | -3
be      | 7.9
be      | -7

有没有办法使用单个流结束Map<String, Pair<BigDecimal, BigDecimal>>

用两个独立的流很容易做到这一点,但我不能只用一个。

【问题讨论】:

  • 那么,您的列表只包含 4 项,2 项为正值,2 项为负值?或者它可能会保存更多或更少的数据?如果是这样,Pair<BigDecimal, BigDecimal> 会保持什么值?我对你的地图的价值部分有点困惑,它代表什么。

标签: java java-stream bigdecimal


【解决方案1】:

它应该使用Collectors.toMap 和合并函数来对对求和。

假设 Pair 是不可变的,并且只有第一个和第二个元素的 getter,代码可能如下所示:

static Map<String, Pair<BigDecimal, BigDecimal>> sumUp(List<Sale> list) {
    return list.stream()
               .collect(Collectors.toMap(
                   Sale::getCountry,
                   sale -> sale.getAmount().signum() >= 0 
                       ? new Pair<>(sale.getAmount(), BigDecimal.ZERO)
                       : new Pair<>(BigDecimal.ZERO, sale.getAmount()),
                   (pair1, pair2) -> new Pair<>(
                       pair1.getFirst().add(pair2.getFirst()),
                       pair1.getSecond().add(pair2.getSecond())
                   )
                   // , LinkedHashMap::new // optional parameter to keep insertion order
               ));
}

测试

List<Sale> list = Arrays.asList(
    new Sale("us", new BigDecimal(100)),
    new Sale("uk", new BigDecimal(-10)),
    new Sale("us", new BigDecimal(-50)),
    new Sale("us", new BigDecimal(200)),
    new Sale("uk", new BigDecimal(333)),
    new Sale("uk", new BigDecimal(-70))
);

Map<String, Pair<BigDecimal, BigDecimal>> map = sumUp(list);

map.forEach((country, pair) -> 
    System.out.printf("%-4s|%s%n%-4s|%s%n", 
        country, pair.getFirst(), country, pair.getSecond()
));

输出

uk  |333
uk  |-80
us  |300
us  |-50

【讨论】:

    【解决方案2】:

    Alex Rudenko 的解决方案,但使用 groupingBy 和下游收集器:

    Map<String, Pair<BigDecimal, BigDecimal>> map =
        list.stream()
            .collect(Collectors.groupingBy(Sale::getCountry,
                       Collectors.mapping(s ->
                          s.getAmount().signum() >= 0?
                          new Pair<>(s.getAmount(), BigDecimal.ZERO):
                          new Pair<>(BigDecimal.ZERO, s.getAmount()),
                       Collectors.reducing(new Pair(BigDecimal.ZERO, BigDecimal.ZERO),
                                           (p1, p2) -> new Pair(p1.getKey().add(p2.getKey()),
                                                                p1.getValue().add(p2.getValue()))))
            ));
    

    【讨论】:

    • 输入列表不能只按国家分组,不能保证每个国家正好有两个Sale,正负amount
    • 是的,@Alex Rudenko。让我稍后再考虑。
    • @Alex Rudenko 无论如何,我找到了接近你的解决方案。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 2020-03-30
    • 2021-12-02
    相关资源
    最近更新 更多