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
