Reputation: 372
I have a list of Transactions whom I wanted to :
My Code snippets looks like :
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()))
));
Transaction Class :
class Transaction {
private int year;
private String type;
private int value;
}
Result Class :
class Result {
private String group;
private int amount;
}
it seems to be not working, what should I do to fix this making sure it works on parallel streams too?
Upvotes: 2
Views: 3711
Reputation: 31868
In the context, Collectors.reducing
would help you reduce two Transaction
objects into a final object of the same type. In your existing code what you could have done to map to Result
type was to use Collectors.mapping
and then trying to reduce it.
But reducing without an identity provides and Optional
wrapped value for a possible absence. Hence your code would have looked like ;
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()))))));
to thanks to Holger, one can simplify this further
…and instead of
Collectors.mapping(func, Collectors.reducing(op))
you can useCollectors.reducing(id, func, op)
Instead of using this and a combination of Collectors.grouping
and Collectors.reducing
, transform the logic to use Collectors.toMap
as:
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()))));
The answer would stand complete with a follow-up read over Java Streams: Replacing groupingBy and reducing by toMap.
Upvotes: 2
Reputation: 3184
I would use a custom collector:
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)
then you can use the collector to get the map:
Map<Integer, Map<String, Result>> collect = transactions.parallelStream().collect(
groupingBy(Transaction::getYear,
groupingBy(Transaction::getType, resultCollector) ) );
Upvotes: 2