首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在groupby之后获得新的列表并对一个属性进行求和

如何在groupby之后获得新的列表并对一个属性进行求和
EN

Stack Overflow用户
提问于 2017-02-12 05:01:09
回答 2查看 1.5K关注 0票数 0

我有一个Settlement类的列表,它具有以下属性:

代码语言:javascript
运行
复制
public class Settlement {
    private String contractNo;
    private String smartNo;
    private String dealTrackNo;
    private String buySellFlag;
    private String cashFlowType;
    private String location;
    private String leaseNo;
    private String leaseName;
    private double volume;
    private double price;
    private double settleAmount;

    // getters and setters
}

现在,我想将Settlement的列表按SmartNo (String)分组,并得到settleAmount上的和,这将成为每个SmartNo的新settleAmount

由于我使用的是Java 8,所以应该使用stream

使用以下代码,Groupby应该是非常直接的:

代码语言:javascript
运行
复制
Map<String, List<Settlement>> map = list.stream()
              .collect(Collectors.groupingBy(Settlement::getSmartNo));
System.out.println(map.getValues());

如果我想在按SmartNo分组并在settlementAmount上求和之后得到一个新的列表呢?这里的大多数例子只显示了如何打印这些数字。我感兴趣的是如何获得聚合列表?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-12 14:16:59

如果我正确理解这个问题,您需要一个具有自定义合并的toMap收集器,如下所示:

代码语言:javascript
运行
复制
list.stream().collect(Collectors.toMap(
       Settlement::getSmartNo,
       Function.identity(),
       (s1, s2) -> s1.addAmount(s2.getSettleAmount())));

Settlement类中使用helper方法:

代码语言:javascript
运行
复制
Settlement addAmount(double addend) {
    this.settleAmount += addend;
    return this;
}
票数 2
EN

Stack Overflow用户

发布于 2017-02-12 08:31:34

我认为不太复杂的方法是在地图的每个values()成员上创建一个新的流,然后是一个map()reduce()。我正在映射到一个新的类AggregatedSettlement,其中只有smartNovolumesettleAmount这三个字段(最后一个字段是和)。然后通过对settleAmount的求和来减少。

代码语言:javascript
运行
复制
    List<AggregatedSettlement> aggregatedList = list.stream()
            .collect(Collectors.groupingBy(Settlement::getSmartNo))
            .values()
            .stream()
            .map(innerList -> innerList.stream()
                    .map(settlm -> new AggregatedSettlement(settlm.getSmartNo(), 
                            settlm.getVolume(), settlm.getSettleAmount()))
                    .reduce((as1, as2) -> {
                        if (as1.getVolume() != as2.getVolume()) {
                            throw new IllegalStateException("Different volumes " + as1.getVolume() 
                                    + " and " + as2.getVolume() + " for smartNo " + as1.getSmartNo());
                        }
                        return new AggregatedSettlement(as1.getSmartNo(), as1.getVolume(), 
                                as1.getSettleAmount() + as2.getSettleAmount());
                    })
                    .get()
            )
            .collect(Collectors.toList());

我对从get()获得的对Optional<AggregatedSettlement>的调用并不太满意;通常您应该避免使用get()。在这种情况下,我知道原始分组只生成至少一个元素的列表,因此reduce()不能提供一个空的可选选项,因此对get()的调用将有效。一个可能的改进是orElseThrow()和一个解释性更强的例外。

我相信有优化的空间。实际上,我生产的AggregatedSettlement对象比我们最终需要的要多得多。和往常一样,在你知道需要优化之前,不要进行优化。

编辑:如果只是为了练习,这里是不构造多余的AggregatedSettlement对象的版本。相反,它从地图中在每个列表上创建两个流,并且它比您的地图长5行:

代码语言:javascript
运行
复制
    List<AggregatedSettlement> aggregatedList = list.stream()
            .collect(Collectors.groupingBy(Settlement::getSmartNo))
            .entrySet()
            .stream()
            .map(entry -> {
                double volume = entry.getValue()
                        .stream()
                        .mapToDouble(Settlement::getVolume)
                        .reduce((vol1, vol2) -> {
                            if (vol1 != vol2) {
                                throw new IllegalStateException("Different volumes " + vol1 
                                        + " and " + vol2 + " for smartNo " + entry.getKey());
                            }
                            return vol1;
                        })
                        .getAsDouble();
                double settleAmountSum = entry.getValue()
                        .stream()
                        .mapToDouble(Settlement::getSettleAmount)
                        .sum();
                return new AggregatedSettlement(entry.getKey(), volume, settleAmountSum);
            })
            .collect(Collectors.toList());

选一个你觉得容易读的。

编辑2:从this answer中可以看出,在Java9中,如果我使用flatMap()代替map(),而使用stream()代替get(),我将能够避免对map()的调用。它将是6个字符多,我可能仍然喜欢它。不过,我还没有尝试过Java9(现在我知道我今天要做什么了:-) get()的优点当然是它会捕获一个编程错误,而内部列表却是空的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42184380

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档