I am pretty new to java moving from c#. I have the following class.
class Resource {
String name;
String category;
String component;
String group;
}
I want to know the following numbers: 1. Count of resources in the category. 2. Distinct count of components in each category. (component names can be duplicate) 3. Count of resources grouped by category and group.
I was able to achieve a little bit of success using Collectors.groupingBy
. However, the result is always like this.
Map<String, List<Resource>>
To get the counts I have to parse the keyset and compute the sizes. Using c# linq, I can easily compute all the above metrics. I am assuming there is definitely a better way to do this in java as well. Please advise.
For #1, I'd use Collectors.groupingBy
along with Collectors.counting
:
Map<String, Long> resourcesByCategoryCount = resources.stream()
.collect(Collectors.groupingBy(
Resource::getCategory,
Collectors.counting()));
This groups Resource
elements by category
, counting how many of them belong to each category.
For #2, I wouldn't use streams. Instead, I'd use the Map.computeIfAbsent
operation (introduced in Java 8):
Map<String, Set<String>> distinctComponentsByCategory = new LinkedHashMap<>();
resources.forEach(r -> distinctComponentsByCategory.computeIfAbsent(
r.getCategory(),
k -> new HashSet<>())
.add(r.getGroup()));
This first creates a LinkedHashMap
(which preserves insertion order). Then, Resource
elements are iterated and put into this map in such a way that they are grouped by category
and each group
is added to a HashSet
that is mapped to each category. As sets don't allow duplicates, there won't be duplicated groups for any category. Then, the distinct count of groups is the size of each set.
For #3, I'd again use Collectors.groupingBy
along with Collectors.counting
, but I'd use a composite key to group by:
Map<List<String>, Long> resourcesByCategoryAndGroup = resources.stream()
.collect(Collectors.groupingBy(
r -> Arrays.asList(r.getCategory(), r.getGroup()), // or List.of
Collectors.counting()));
This groups Resource
elements by category
and group
, counting how many of them belong to each (category, group)
pair. For the grouping key, a two-element List<String>
is being used, with the category
being its 1st element and the component
being its 2nd element.
Or, instead of using a composite key, you could use nested grouping:
Map<String, Map<String, Long>> resourcesByCategoryAndGroup = resources.stream()
.collect(Collectors.groupingBy(
Resource::getCategory,
Collectors.groupingBy(
Resource::getGroup,
Collectors.counting())));