Groupby counts in java

Anan :

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.

Federico Peralta Schaffner :

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())));

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324179&siteId=1