Java 8:AveragingDouble和映射到对象的方法

问题描述 投票:0回答:1

我有如下所示的事务对象列表。

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 Transaction {
    private final Trader trader;
    private final int year;
    private final int value;
    private String type;
    private String method;
}

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

在计算之前,对列表进行以下操作。

  • 按年份对交易进行分组
  • 然后按方法分组

而最后产生的对象应该有平均数、年份和方法的值。

结果类。

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() 产生了一个双倍的结果,而所有其他信息都在这个映射中丢失了。

有什么办法可以抓住这些字段或将结果正确地映射到一个对象中吗?

lambda java-8 java-stream collectors
1个回答
3
投票

你可以这样做。

在按以下方式分组之后 办法mapping 采集器 Result 对象 Collectors.mapping(t->new Result(...),)你知道的 mapping 采集器 collector 作为第二个参数。因为你想在分组后将它们合并,计算平均值。collectingAndThen 采集器是在这里执行的最佳选择。collectingAndThen 收集到列表后,以一个函数作为终点(平均函数),计算列表中所有元素的平均值。

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

1
投票

你可以用 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())))
        ));

产出:

{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']}}
© www.soinside.com 2019 - 2024. All rights reserved.