Java8 Stream (12) Collectors.groupingBy Detailed explanation of grouping statistics


It is very important to be able to skillfully use groupingBy, reducing, and toMap in Collectors at work, because these clever functions can greatly improve development efficiency, so it is urgent to learn them well. First prepare a List collection for testing.

public static List<User> getUserList() {
    
    
    List<User> users = new ArrayList<>();
    users.add(new User("1", "name1", "Java组", 33, "男", new BigDecimal("25000"), true));
    users.add(new User("2", "name2", "Java组", 31, "女", new BigDecimal("28000"), true));
    users.add(new User("3", "name3", "前端组", 33, "男", new BigDecimal("18000"), true));
    users.add(new User("4", "name4", "前端组", 25, "男", new BigDecimal("19000"), false));
    users.add(new User("5", "name5", "QA组", 24, "女", new BigDecimal("15000"), true));
    users.add(new User("6", "name6", "产品组", 34, "女", new BigDecimal("12000"), true));
    return users;
}

1 List to Map

1.1 Grouping using groupingBy

Group by department

Map<String, List<User>> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept
        )
);

Group by custom Key

Map<String, List<User>> groupByDeptAppendName = users.stream().collect(
        Collectors.groupingBy(
                user -> user.getDept() + "&" + user.getName()
        )
);

multi-level grouping

 Map<String, Map<String, List<User>>> groupByDeptAndGender = users.stream()
         .filter(user -> Objects.nonNull(user.getSex())) // group by 的字段不能有null值
         .collect(
                 Collectors.groupingBy(
                         User::getDept,
                         Collectors.groupingBy(User::getSex)
                 )
         );

Group by department and find the list of IDs

Map<String, List<String>> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.mapping(
                        User::getId,
                        Collectors.toList()
                )
        )
);
{
    
    Java组=[1, 2], QA=[5], 前端组=[3, 4], 产品组=[6]}

Group by department, Count the number of people

Map<String, Long> groupBuyDeptThenCount = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.counting()
        )
);

According to department grouping, find the Set of Sex

Map<String, Set<String>> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.mapping(
                        User::getSex,
                        Collectors.toSet()
                )
        )
);
{
    
    Java组=[女, 男], QA组=[], 前端组=[], 产品组=[]}

According to department grouping, find the number of duplicates in Sex

Collectors.collectingAndThen()It accepts two parameters: downstreamand finisher. in

  • downstreamIs a Collector, used to collect elements in the data stream;
  • finisherIs a Function function used to downstreamprocess the collection results and return the final results.
Map<String, Integer> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.mapping(
                                User::getSex,
                                Collectors.toSet()
                        ),
                        a -> a.size()
                )
        )
);
{
    
    Java组=2, QA组=1, 前端组=1, 产品组=1}

1.2 Use partitioningBy partitioning

Map<Boolean, List<User>> collect = users.stream().collect(
        Collectors.partitioningBy(
                a -> a.getAge() > 30
        )
);

1.3 Using toMap

List 转 Map<ID, User>

Map<String, User> userMap = users.stream().collect(
                Collectors.toMap(
                        User::getId,
                        Function.identity(),
                        (k1, k2) -> k1 //key重复,用第一个
                )
        );

List 转 Map<ID, Name>

Map<String, String> idToName = users.stream().collect(
        Collectors.toMap(
                User::getId,
                User::getName
        )
);

2 Find the maximum, minimum, average, and sum

2.1 No grouping, direct statistics

求年龄最大的人:
Optional<User> maxAgeUserOptional = users.stream().collect(
        Collectors.maxBy(Comparator.comparing(User::getAge))
);

求年龄最小的人:
Optional<User> minAgeUserOptional = users.stream().collect(
        Collectors.minBy(Comparator.comparing(User::getAge))
);

求最大的年龄:
int maxAge = users.stream().mapToInt(User::getAge).max().getAsInt();

求最小的年龄:
int minAge = users.stream().mapToInt(User::getAge).min().getAsInt();

求年龄总和:
int sumAge = users.stream().mapToInt(User::getAge).sum();

求平均年龄:
double avgAge = users.stream().mapToInt(User::getAge).average().getAsDouble();

2.2 Group first, then count

Find the person with the greatest age in each department

Map<String, User> groupByDeptThenGetMaxAgeUser = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.maxBy(
                                Comparator.comparing(
                                        User::getAge,
                                        Comparator.nullsLast(Integer::compareTo)
                                )
                        ),
                        Optional::get
                )
        )
);

Find the maximum value of Age in each department

Map<String, Integer> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.maxBy(
                                Comparator.comparingInt(User::getAge)
                        ),
                        a -> a.isPresent() ? a.get().getAge() : null
                )

        )
);

Find the average age in each department

Map<String, Double> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.averagingInt(User::getAge)

        )
);

Find the sum of Age in each department

 Map<String, Integer> collect = users.stream().collect(
         Collectors.groupingBy(
                 User::getDept,
                 Collectors.summingInt(User::getAge)
         )
 );

Statistics using IntSummaryStatistics

Map<String, IntSummaryStatistics> collect = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.summarizingInt(
                        User::getAge
                )
        )
);
for (Map.Entry<String, IntSummaryStatistics> entry : collect.entrySet()) {
    
    
    IntSummaryStatistics summaryStatistics = entry.getValue();
    System.out.println("----------------key----------------" + entry.getKey());
    System.out.println("求和:" + summaryStatistics.getSum());
    System.out.println("求平均" + summaryStatistics.getAverage());
    System.out.println("求最大:" + summaryStatistics.getMax());
    System.out.println("求最小:" + summaryStatistics.getMin());
    System.out.println("求总数:" + summaryStatistics.getCount());
}

3 BigDecimal type processing

3.1 No grouping, direct statistics

List<BigDecimal> userSalary = users.stream().map(User::getSalary).collect(Collectors.toList());

Optional<BigDecimal> maxSalary = userSalary.stream().reduce(BigDecimal::max);
Optional<BigDecimal> minSalary = userSalary.stream().reduce(BigDecimal::min);
BigDecimal sumSalary = userSalary.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal avgSalary= sumSalary.divide(BigDecimal.valueOf(userSalary.size()), 2, BigDecimal.ROUND_HALF_UP);

3.2 Group first, then count

Looking for the biggest salary from each department
Map<String, BigDecimal> groupByDeptThenGetMaxSalary = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.reducing(
                        BigDecimal.ZERO,
                        User::getSalary,
                        BigDecimal::max
                )
        )
);
Looking for the smallest salary from each department
Map<String, BigDecimal> groupByDeptThenGetMinSalary = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.reducing(
                        BigDecimal.valueOf(Long.MAX_VALUE),
                        User::getSalary,
                        BigDecimal::min
                )
        )
);

如果考虑Salarynull值,可以如下处理
Map<String, BigDecimal> groupByDeptThenGetMinSalary = users.stream().collect(
        Collectors.groupingBy(
                User::getDept,
                Collectors.collectingAndThen(
                        Collectors.reducing(
                                (c1, c2) -> c1.getSalary().compareTo(c2.getSalary()) > 0 ? c2 : c1
                        ),
                        a -> a.isPresent() ? a.get().getSalary() : null
                )
        )
);
Find the total salary of each department
Map<String, BigDecimal> groupByDeptThenGetSumSalary = users.stream()
        .filter(user -> Objects.nonNull(user.getDept())) //比较的字段不能有null值
        .collect(
                Collectors.groupingBy(
                        User::getDept,
                        Collectors.reducing(
                                BigDecimal.ZERO,
                                User::getSalary,
                                BigDecimal::add
                        )
                )
        );
Find the salary average for each department

CollectorsThere is averagingInt、averagingLong、averagingDoublewaiting, but no averagingBigDecimal.
Refer to java lambada to group and summarize the list to implement custom averagingBigDecimal

Map<String, BigDecimal> groupByDeptThenGetAvgSalary = users.stream()
        .filter(user -> Objects.nonNull(user.getDept())) //比较的字段不能有null值
        .collect(
                Collectors.groupingBy(
                        User::getDept,
                        CustomCollectors.averagingBigDecimal(User::getSalary, 2, 2)
                )
        );

Guess you like

Origin blog.csdn.net/winterking3/article/details/131671534