本文目录导读:

我来给你展示 groupingBy 的多个实用案例,从基础到高级:
基础分组
import java.util.*;
import java.util.stream.Collectors;
public class GroupingByDemo {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("张三", 25, "北京"),
new Person("李四", 30, "上海"),
new Person("王五", 25, "北京"),
new Person("赵六", 35, "广州"),
new Person("孙七", 30, "深圳"),
new Person("周八", 25, "上海")
);
// 1. 按城市分组
Map<String, List<Person>> byCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity));
System.out.println("按城市分组: " + byCity);
// 2. 按年龄分组
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
System.out.println("按年龄分组: " + byAge);
}
}
class Person {
private String name;
private int age;
private String city;
public Person(String name, int age, String city) {
this.name = name;
this.age = age;
this.city = city;
}
// getter/setter
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
@Override
public String toString() {
return name + "(" + age + "岁)";
}
}
分组后计数
public class GroupingCountDemo {
public static void main(String[] args) {
List<String> words = Arrays.asList(
"apple", "banana", "apple", "orange",
"banana", "grape", "apple"
);
// 统计每个单词出现次数
Map<String, Long> wordCount = words.stream()
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
System.out.println("单词计数: " + wordCount);
// 按字符串长度分组并计数
Map<Integer, Long> lengthCount = words.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.counting()
));
System.out.println("按长度计数: " + lengthCount);
}
}
分组后聚合操作
public class GroupingAggregationDemo {
public static void main(String[] args) {
List<Order> orders = Arrays.asList(
new Order("A001", "电子产品", 1000.0),
new Order("A002", "服装", 200.0),
new Order("A003", "电子产品", 1500.0),
new Order("A004", "食品", 100.0),
new Order("A005", "服装", 350.0),
new Order("A006", "电子产品", 800.0)
);
// 1. 按类别求销售额总和
Map<String, Double> totalByCategory = orders.stream()
.collect(Collectors.groupingBy(
Order::getCategory,
Collectors.summingDouble(Order::getAmount)
));
System.out.println("各类别销售额: " + totalByCategory);
// 2. 按类别求平均销售额
Map<String, Double> avgByCategory = orders.stream()
.collect(Collectors.groupingBy(
Order::getCategory,
Collectors.averagingDouble(Order::getAmount)
));
System.out.println("各类别平均销售额: " + avgByCategory);
// 3. 按类别求最高销售额
Map<String, Optional<Order>> maxByCategory = orders.stream()
.collect(Collectors.groupingBy(
Order::getCategory,
Collectors.maxBy(Comparator.comparingDouble(Order::getAmount))
));
System.out.println("各类别最高订单: " + maxByCategory);
// 4. 按类别汇总统计信息
Map<String, DoubleSummaryStatistics> statsByCategory = orders.stream()
.collect(Collectors.groupingBy(
Order::getCategory,
Collectors.summarizingDouble(Order::getAmount)
));
System.out.println("电子产品统计: " + statsByCategory.get("电子产品"));
}
}
class Order {
private String id;
private String category;
private double amount;
public Order(String id, String category, double amount) {
this.id = id;
this.category = category;
this.amount = amount;
}
public String getCategory() { return category; }
public double getAmount() { return amount; }
@Override
public String toString() {
return id + "(" + category + "): " + amount;
}
}
多级分组
public class MultiGroupingDemo {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("张三", "技术部", "北京", 25),
new Employee("李四", "市场部", "上海", 30),
new Employee("王五", "技术部", "北京", 28),
new Employee("赵六", "财务部", "广州", 35),
new Employee("孙七", "市场部", "北京", 27),
new Employee("周八", "技术部", "上海", 32)
);
// 多级分组:先按部门,再按城市
Map<String, Map<String, List<Employee>>> byDeptAndCity = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(Employee::getCity)
));
System.out.println("多级分组结果: ");
byDeptAndCity.forEach((dept, cityMap) -> {
System.out.println(" " + dept + ": " + cityMap);
});
// 多级分组:先按城市,再按年龄段
Map<String, Map<String, List<Employee>>> byCityAndAgeGroup = employees.stream()
.collect(Collectors.groupingBy(
Employee::getCity,
Collectors.groupingBy(emp -> {
if (emp.getAge() < 30) return "30岁以下";
else return "30岁及以上";
})
));
System.out.println("\n按城市和年龄段分组: ");
byCityAndAgeGroup.forEach((city, ageMap) -> {
System.out.println(" " + city + ": " + ageMap);
});
}
}
class Employee {
private String name;
private String department;
private String city;
private int age;
public Employee(String name, String department, String city, int age) {
this.name = name;
this.department = department;
this.city = city;
this.age = age;
}
public String getDepartment() { return department; }
public String getCity() { return city; }
public int getAge() { return age; }
@Override
public String toString() {
return name + "(" + age + "岁)";
}
}
分组后转换为其他集合类型
public class GroupingCollectDemo {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("001", "张三", 85, "一班"),
new Student("002", "李四", 92, "二班"),
new Student("003", "王五", 78, "一班"),
new Student("004", "赵六", 88, "二班"),
new Student("005", "孙七", 95, "一班")
);
// 1. 分组后只收集姓名到Set
Map<String, Set<String>> namesByClass = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.mapping(Student::getName, Collectors.toSet())
));
System.out.println("按班级收集姓名: " + namesByClass);
// 2. 分组后收集为特定Map类型
Map<String, List<Student>> treeMapResult = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
TreeMap::new, // 指定Map类型
Collectors.toList()
));
System.out.println("TreeMap结果: " + treeMapResult);
// 3. 分组后拼接姓名
Map<String, String> joinedNames = students.stream()
.collect(Collectors.groupingBy(
Student::getClassName,
Collectors.mapping(Student::getName,
Collectors.joining(", ", "[", "]"))
));
System.out.println("拼接姓名: " + joinedNames);
}
}
class Student {
private String id;
private String name;
private int score;
private String className;
public Student(String id, String name, int score, String className) {
this.id = id;
this.name = name;
this.score = score;
this.className = className;
}
public String getName() { return name; }
public String getClassName() { return className; }
@Override
public String toString() {
return name + "(成绩:" + score + ")";
}
}
分组后排序和限制
public class GroupingSortLimitDemo {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product("手机", "电子", 5000),
new Product("电脑", "电子", 8000),
new Product("耳机", "电子", 500),
new Product("T恤", "服装", 100),
new Product("牛仔裤", "服装", 300),
new Product("面包", "食品", 15),
new Product("牛奶", "食品", 10)
);
// 每个类别中价格最高的商品
Map<String, Optional<Product>> topByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.maxBy(Comparator.comparingInt(Product::getPrice))
));
System.out.println("各类别最贵商品: ");
topByCategory.forEach((cat, prod) ->
System.out.println(" " + cat + ": " + prod.orElse(null)));
// 每个类别的商品数量,按数量排序
Map<String, Long> countByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.counting()
));
// 排序输出
countByCategory.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.forEach(entry ->
System.out.println(entry.getKey() + ": " + entry.getValue() + "件"));
}
}
class Product {
private String name;
private String category;
private int price;
public Product(String name, String category, int price) {
this.name = name;
this.category = category;
this.price = price;
}
public String getCategory() { return category; }
public int getPrice() { return price; }
@Override
public String toString() {
return name + "(" + price + "元)";
}
}
实际业务场景示例
public class BusinessScenarioDemo {
public static void main(String[] args) {
// 模拟电商订单数据
List<Order> orders = generateOrders();
// 场景1:统计每个地区的订单数量和总金额
Map<String, Map<String, Object>> regionStats = orders.stream()
.collect(Collectors.groupingBy(
Order::getRegion,
Collectors.collectingAndThen(
Collectors.toList(),
list -> {
Map<String, Object> stats = new HashMap<>();
stats.put("订单数", list.size());
stats.put("总金额", list.stream()
.mapToDouble(Order::getAmount)
.sum());
stats.put("客户数", list.stream()
.map(Order::getCustomerId)
.distinct()
.count());
return stats;
}
)
));
System.out.println("各地区统计: ");
regionStats.forEach((region, stats) ->
System.out.println(" " + region + ": " + stats));
// 场景2:找出每个客户购买最多的商品类别
Map<String, Optional<String>> favoriteCategory = orders.stream()
.collect(Collectors.groupingBy(
Order::getCustomerId,
Collectors.groupingBy(Order::getCategory, Collectors.counting())
))
.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
));
System.out.println("\n客户偏好: ");
favoriteCategory.forEach((customer, cat) ->
System.out.println(" " + customer + ": " + cat.orElse("无")));
}
private static List<Order> generateOrders() {
List<Order> orders = new ArrayList<>();
Random random = new Random();
String[] regions = {"北京", "上海", "广州", "深圳"};
String[] categories = {"电子产品", "服装", "食品", "家居"};
String[] customers = {"C001", "C002", "C003", "C004"};
for (int i = 0; i < 100; i++) {
orders.add(new Order(
"O" + System.currentTimeMillis() + i,
customers[random.nextInt(customers.length)],
categories[random.nextInt(categories.length)],
regions[random.nextInt(regions.length)],
10 + random.nextInt(990)
));
}
return orders;
}
}
class Order {
private String orderId;
private String customerId;
private String category;
private String region;
private double amount;
public Order(String orderId, String customerId, String category,
String region, double amount) {
this.orderId = orderId;
this.customerId = customerId;
this.category = category;
this.region = region;
this.amount = amount;
}
public String getCustomerId() { return customerId; }
public String getCategory() { return category; }
public String getRegion() { return region; }
public double getAmount() { return amount; }
}
- 基础分组:
groupingBy(Function)按单个字段分组 - 分组计数:配合
counting()统计数量 - 分组合计:配合
summingInt/Double计算总和 - 多级分组:嵌套
groupingBy实现多维度分组 - 分组转换:使用
mapping转换元素类型 - 指定Map类型:使用
TreeMap等保证顺序 - 聚合操作:
maxBy,minBy,averagingInt等
这些示例涵盖了日常开发中95%以上的 groupingBy 使用场景。