Search code examples
javajava-stream

Java Stream collectors: how to create total with counting


I have a list of objects of class Result defined as:

class Result {
    String code;
    String description;
    Duration value;
}

and these two classes:

class Product {
    String code;
    String description;
}

class Total {
    Duration total;
    long count;
}

Given the List<Result> I have to generate a Map<Product, Total> where in total I have the sum of every value in Result that has the same code and description (this is key) and as count how many result with this key is present in the list of results.

For creating the total sum I consider to use plus(Duration) of Duration.

How can I do this?
Thanks for the help.

UPDATE:

From the comments hints I was able to do this:
(Can someone tell me if it can be correct?)

Map <Product, Total> map = results.stream()
                                  .collect(Collectors.groupingBy(p -> new Product(p.getCode(), p.getDescription()),
    Collectors.collectingAndThen(Collectors.toList(), list -> {
        Duration total = list.stream().map(Result::getValue).reduce(Duration.ZERO, Duration::plus);
        long count = list.size();
        return new Total(total, count);
    })));

Solution

  • Your approach is working and formally correct, but it is wasteful to collect all elements into Lists, just to perform another Reduction afterwards. Perform the Reduction in the first place:

    Map<Product, Total> map = results.stream()
      .collect(Collectors.groupingBy(r -> new Product(r.getCode(), r.getDescription()),
            Collectors.teeing(
                Collectors.reducing(Duration.ZERO, Result::getValue, Duration::plus),
                Collectors.counting(),
                Total::new)));