You can use Collectors.collectingAndThento add a shared record to each internal map:
Map<LocalDate, Map<String, Long>> result = myProductList.stream()
.collect(Collectors.groupingBy(
Product::getDate,
TreeMap::new,
Collectors.collectingAndThen(
Collectors.groupingBy(
Product::getProductName,
LinkedHashMap::new,
Collectors.counting()),
map -> {
map.put("Total", map.values().stream().mapToLong(c -> c).sum());
return map;
})));
The map resultcontains:
{2017-05-01={ABC=2, XYZ=1, Total=3}, 2017-05-02={ABC=1, Total=1}}
, . - TreeMap, ( ). LinkedHashMap, , .. , .
. , , , , , . ( , map.values().stream().mapToLong(c -> c).sum()). , , 1 , . , :
public static <T, K> Collector<T, ?, Map<K, Long>> groupsWithTotal(
Function<? super T, ? extends K> classifier,
K totalKeyName) {
class Acc {
Map<K, Long> map = new LinkedHashMap<>();
long total = 0L;
void accumulate(T elem) {
this.map.merge(classifier.apply(elem), 1L, Long::sum);
this.total++;
}
Acc combine(Acc another) {
another.map.forEach((k, v) -> {
this.map.merge(k, v, Long::sum);
this.total += v;
});
return this;
}
Map<K, Long> finish() {
this.map.put(totalKeyName, total);
return this.map;
}
}
return Collector.of(Acc::new, Acc::accumulate, Acc::combine, Acc::finish);
}
(, Collectors.groupingBy(Product::getProductName, Collectors.counting())), . .
, groupsWithTotal:
Map<LocalDate, Map<String, Long>> result = myProductList.stream()
.collect(Collectors.groupingBy(
Product::getDate,
TreeMap::new,
groupsWithTotal(Product::getProductName, "Total")));
:
{2017-05-01={ABC=2, XYZ=1, Total=3}, 2017-05-02={ABC=1, Total=1}}
, LinkedHashMap null , null, , a Product null productName, null , NullPointerException.