Java 8:AveragingDouble 和映射到对象
Java 8: AveragingDouble and mapping to 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
对象中,其中包含一些关于正在计算其平均值的组的附加数据等。
在计算之前。对列表执行以下操作:
- 按年份对交易进行分组
- 然后按方法分组
最终生成的对象应该有平均值、年份和方法的值。
结果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()
会产生一个双精度值,并且所有其他信息都在此映射中丢失。
有什么方法可以抓取这些字段或将结果正确映射到对象中吗?
你可以这样做:
按 年份 和 方法 分组后,使用 mapping
收集器创建 Result
对象作为 Collectors.mapping(t->new Result(...),)
,如您所知,mapping
收集器将 collector
作为第二个参数。因为要在分组后合并并计算平均值,collectingAndThen
collector 是执行这里的最佳选择。确实 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());
您可以使用 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']}}
我有如下所示的交易对象列表:
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
对象中,其中包含一些关于正在计算其平均值的组的附加数据等。
在计算之前。对列表执行以下操作:
- 按年份对交易进行分组
- 然后按方法分组
最终生成的对象应该有平均值、年份和方法的值。
结果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()
会产生一个双精度值,并且所有其他信息都在此映射中丢失。
有什么方法可以抓取这些字段或将结果正确映射到对象中吗?
你可以这样做:
按 年份 和 方法 分组后,使用 mapping
收集器创建 Result
对象作为 Collectors.mapping(t->new Result(...),)
,如您所知,mapping
收集器将 collector
作为第二个参数。因为要在分组后合并并计算平均值,collectingAndThen
collector 是执行这里的最佳选择。确实 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());
您可以使用 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']}}