繁体   English   中英

Java 8:平均双并映射到 object

[英]Java 8: AveragingDouble and mapping to object

我有交易 object 的列表,如下所示:

List<Transaction> transactions = Arrays.asList(
                new Transaction(brian, 2011, 300, "X", "M1"),
                new Transaction(raoul, 2012, 1000, "T", "M1"),
                new Transaction(raoul, 2011, 400, "S", "M2"),
                new Transaction(mario, 2012, 710, "X", "M1"),
                new Transaction(mario, 2012, 700, "X", "M2"),
                new Transaction(alan, 2012, 950, "T", "M1")
        );

事务 Class:

class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;
    private String type;
    private String method;
}

现在我需要根据每个组的事务类型找到平均值,最后将平均值转储到Result object 中,其中包含一些关于正在计算其平均值的组的附加数据等。

在计算之前。 对列表执行以下操作:

  • 将交易与年份分组
  • 然后按方法分组

并且最终生产的object应该有平均值,年份和方法的价值。

结果 Class:

class Result {
    private Double avg;
    private int year;
    private String method;
}

代码:

 Map<Integer, Map<String, Result>> res = transactions.stream().collect(
                groupingBy(Transaction::getYear,
                        groupingBy(Transaction::getMethod),
                            collectingAndThen( averagingDouble( t -> "X".equals(t.getType()) ? 1: 0 ),
                                    v -> new Result(v, GROUP_METHOD? , GROUP_YEAR?))
                )
        );

但是 GROUP_METHOD 和 GROUP_YEAR 值在此处无法根据分组进行访问,因为averagingDouble()产生一个双精度值,并且所有其他信息都在此映射中丢失。

有什么方法可以获取这些字段或正确地将 map 结果转换为 object?

你可以这样做:

在使用mapping收集器按年份方法分组后,创建Result object 作为Collectors.mapping(t->new Result(...),) ,正如您所知, mapping收集器将collector作为第二个参数。 因为要在分组后合并它们并计算平均值,所以collectingAndThen器是在这里执行它的最佳选择。 确实 collectAndThen 在collectingAndThen到列表后将 function 作为整理器(平均函数)并计算列表中所有元素的平均值。

transactions.stream().collect(
      groupingBy(Transaction::getYear,
           groupingBy(Transaction::getMethod,
               mapping(t -> new Result((double) t.getValue(), t.getYear(), t.getMethod()),
                        collectingAndThen(Collectors.toList(), average::apply)))));

function 的平均值为:

 Function<List<Result>, Result> average = l -> new Result(l.stream()
            .mapToDouble(Result::getAvg)
            .average().orElse(0d), l.get(0).getYear(), l.get(0).getMethod());

您可以使用averagingDouble收集到Map<Integer, Map<String, Double>> ,如 OP 中所示:

Map<Integer, Map<String, Double>> doubleMap = transactions.stream()
                .collect(groupingBy(Transaction::getYear,
                        groupingBy(Transaction::getMethod,
                                averagingDouble(Transaction::getValue))));

然后重新映射到Map<Integer, Map<String, Result>>

Map<Integer, Map<String, Result>> resultMap = doubleMap.entrySet().stream()
      .flatMap(my -> my.getValue().entrySet().stream()
             .map(mm -> new Result(mm.getValue(), my.getKey(), mm.getKey())))
      .collect(groupingBy(Result::getYear, toMap(Result::getMethod, Function.identity())));

或在使用collectingAndThen的单个语句中:

 Map<Integer, Map<String, Result>> collect1 = transactions.stream()
     .collect(collectingAndThen(
         groupingBy(Transaction::getYear,
            groupingBy(Transaction::getMethod,averagingDouble(Transaction::getValue))),
         map -> map.entrySet().stream()
                 .flatMap(my -> my.getValue().entrySet().stream()
                    .map(mm -> new Result(mm.getValue(), my.getKey(), mm.getKey())))
                 .collect(groupingBy(Result::getYear, 
                                     toMap(Result::getMethod, Function.identity())))
        ));

output:

{2011={M1=Result[avg=300.0, year=2011, method='M1'], M2=Result[avg=400.0, year=2011, method='M2']},
 2012={M1=Result[avg=886.6666666666666, year=2012, method='M1'], M2=Result[avg=700.0, year=2012, method='M2']}}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM