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: downstream
and finisher
. in
downstream
Is a Collector, used to collect elements in the data stream;finisher
Is a Function function used todownstream
process 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
)
)
);
如果考虑Salary有null值,可以如下处理
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
Collectors
There is averagingInt、averagingLong、averagingDouble
waiting, 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)
)
);