Home > Back-end >  Java Streams API - Impose Ordering while collecting with groupingBy()
Java Streams API - Impose Ordering while collecting with groupingBy()

Time:08-02

I am trying to group the following list of Foo objects by getName() and count occurrences while ordering by getNumber() in ascending order.

Input:

List<Foo> fooList = List.of(
    new Foo("Alpha", 1.1f),
    new Foo("Delta", 1.2f),
    new Foo("Charlie", 3.1f),
    new Foo("Alpha", 2.1f),
    new Foo("Charlie", 4.1f),
    new Foo("Delta", 2.2f)
);

Map Output:

{Alpha=2, Delta=2, Charlie=2}

My current solution is as follows:

Map<String, Integer> map = new LinkedHashMap<>();

fooList
    .stream()
    .sorted(Comparator.comparingDouble(Foo::getNumber))
    .forEach(foo -> {
        int val = map.getOrDefault(foo.getName(), 0);
        map.put(foo.getName(), val   1); 
    });

Is there any better way with Collectors.groupingBy()?

CodePudding user response:

Is there any better way with Collectors.groupingBy()?

You need the flavor of groupingBy(classifier,mapFactory,downstream) which allows to specify a mapFactory.

As a downstream collector, we need to apply combination of collectingAndThen() and counting() because the resulting type produced by counting() is Long and we need to turn it into Integer.

Map<String, Integer> fooNameByCount = fooList.stream()
    .sorted(Comparator.comparing(Foo::getNumber))
    .collect(Collectors.groupingBy(
        Foo::getName,
        LinkedHashMap::new,
        Collectors.collectingAndThen(Collectors.counting(),
            Long::intValue)
    ));

We can achieve the same result by using a single collector with the following version of toMap(keyMapper,valueMapper,mergeFunction,mapFactory):

Map<String, Integer> fooNameByCount = fooList.stream()
    .sorted(Comparator.comparing(Foo::getNumber))
    .collect(Collectors.toMap(
        Foo::getName,
        foo -> 1,
        Integer::sum,
        LinkedHashMap::new
    ));

Also note that using collector is always a preferred way to accumulate the result.

Usage of forEach() such cases when you generate the result by the means of collect(), reduce(), etc. is discouraged by the API documentation

  • Related