I think the not-too-complex way through is a new stream on each member of the values() of your map and then a map() and reduce(). I am mapping to a new class AggregatedSettlement with just the three fields smartNo, volume and settleAmount (the last will be the sum). And then reducing by summing the settleAmounts.
List<AggregatedSettlement> aggregatedList = list.stream()
.collect(Collectors.groupingBy(Settlement::getSmartNo))
.values()
.stream()
.map(innerList -> innerList.stream()
.map(settlm -> new AggregatedSettlement(settlm.getSmartNo(),
settlm.getVolume(), settlm.getSettleAmount()))
.reduce((as1, as2) -> {
if (as1.getVolume() != as2.getVolume()) {
throw new IllegalStateException("Different volumes " + as1.getVolume()
+ " and " + as2.getVolume() + " for smartNo " + as1.getSmartNo());
}
return new AggregatedSettlement(as1.getSmartNo(), as1.getVolume(),
as1.getSettleAmount() + as2.getSettleAmount());
})
.get()
)
.collect(Collectors.toList());
I am not too happy about the call to get() on the Optional<AggregatedSettlement> that I get from reduce(); usually you should avoid get(). In this case I know that the original grouping only produced lists of at least one element, so the the reduce() cannot give an empty optional, hence the call to get() will work. A possible refinement would be orElseThrow() and a more explanatory exception.
I am sure there’s room for optimization. I am really producing quite many more AggregatedSettlement objects than we need in the end. As always, don’t optimize until you know you need to.
Edit: If only for the exercise here’s the version that doesn’t construct superfluous AggregatedSettlement objects. Instead it creates two streams on each list from your map, and it’s 5 lines longer:
List<AggregatedSettlement> aggregatedList = list.stream()
.collect(Collectors.groupingBy(Settlement::getSmartNo))
.entrySet()
.stream()
.map(entry -> {
double volume = entry.getValue()
.stream()
.mapToDouble(Settlement::getVolume)
.reduce((vol1, vol2) -> {
if (vol1 != vol2) {
throw new IllegalStateException("Different volumes " + vol1
+ " and " + vol2 + " for smartNo " + entry.getKey());
}
return vol1;
})
.getAsDouble();
double settleAmountSum = entry.getValue()
.stream()
.mapToDouble(Settlement::getSettleAmount)
.sum();
return new AggregatedSettlement(entry.getKey(), volume, settleAmountSum);
})
.collect(Collectors.toList());
Pick the one you find easier to read.
Edit 2: It seems from this answer that in Java 9 I will be able to avoid the call to Optional.get() if instead of map() I use flatMap() and instead of get() I use stream(). It will be 6 chars longer, I may still prefer it. I haven’t tried Java 9 yet, though (now I know what I’m going to do today :-) The advantage of get() is of course that it would catch a programming error where the inner list comes out empty.