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.grouping
和 Collectors.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()))));
通过后续阅读 ,答案将会完整。
我有一份我想要的交易清单:
- 年份第一组
- 然后为当年的每笔交易按类型分组
- 然后将交易转换为具有子组中所有交易价值总和的结果对象。
我的代码片段如下所示:
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 useCollectors.reducing(id, func, op)
不要使用这个以及 Collectors.grouping
和 Collectors.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()))));
通过后续阅读