Java 8 Lambda - 分组和减少对象

Java 8 Lambda - Grouping & Reducing Object

我有一份我想要的交易清单:

我的代码片段如下所示:

Map<Integer, Map<String, Result> res = transactions.stream().collect(Collectors
                            .groupingBy(Transaction::getYear,
                                groupingBy(Transaction::getType),
                                  reducing((a,b)-> new Result("YEAR_TYPE", a.getAmount() + b.getAmount()))
));

交易Class:

class Transaction {

    private int year;
    private String type;
    private int value;
}

结果Class:

class Result {

    private String group;
    private int amount;
}

它似乎不起作用,我应该如何解决这个问题以确保它也适用于并行流?

我会使用自定义收集器:

Collector<Transaction, Result, Result> resultCollector =
 Collector.of(Result::new, // what is the result of this collector

  (a, b) -> { a.setAmount( a.getAmount() + b.getValue());
              a.setGroup("YEAR_TYPE"); }, // how to accumulate a result from a transaction

  (l, r) -> { l.setAmount(l.getAmount() + r.getAmount()); return l; }); // how to combine two 
                                                                        // result instances 
                                                                        // (used in parallel streams)

然后就可以使用采集器获取地图了:

Map<Integer, Map<String, Result>> collect = transactions.parallelStream().collect(
       groupingBy(Transaction::getYear,
               groupingBy(Transaction::getType, resultCollector) ) );

在上下文中,Collectors.reducing 将帮助您将两个 Transaction 对象缩减为同一类型的最终对象。在您现有的代码中,您可以使用 Collectors.mapping 映射到 Result 类型,然后尝试减少它。

但是在没有身份的情况下减少提供和 Optional 包装价值可能缺席。因此,您的代码看起来像 ;

Map<Integer, Map<String, Optional<Result>>> res = transactions.stream()
        .collect(Collectors.groupingBy(Transaction::getYear,
                Collectors.groupingBy(Transaction::getType,
                        Collectors.mapping(t -> new Result("YEAR_TYPE", t.getValue()),
                                Collectors.reducing((a, b) ->
                                        new Result(a.getGroup(), a.getAmount() + b.getAmount()))))));

感谢 Holger,可以进一步简化此

…and instead of Collectors.mapping(func, Collectors.reducing(op)) you can use Collectors.reducing(id, func, op)


不要使用这个以及 Collectors.groupingCollectors.reducing 的组合,而是将逻辑转换为使用 Collectors.toMap 作为:

Map<Integer, Map<String, Result>> result = transactions.stream()
        .collect(Collectors.groupingBy(Transaction::getYear,
                Collectors.toMap(Transaction::getType,
                        t -> new Result("YEAR_TYPE", t.getValue()),
                        (a, b) -> new Result(a.getGroup(), a.getAmount() + b.getAmount()))));

通过后续阅读 ,答案将会完整。