在 Java 8 中,Stream API 提供了强大的功能来处理集合数据,其中包括对 List 进行分组操作。通过使用 Collectors.groupingBy,可以轻松地将 List 中的元素按照某个条件分组。本文将详细介绍如何使用 Java 8 对 List 进行分组,并提供多种示例代码。
1. 使用 Collectors.groupingBy 进行分组
Collectors.groupingBy 是 Java 8 中最常用的分组方法。它可以将 List 中的元素按照指定的条件分组,并返回一个 Map,其中键是分组的条件,值是对应的元素列表。
1.1 基本分组
假设有一个 List,需要按照 Person 的 city 属性进行分组。
import java.util.*;
import java.util.stream.Collectors;
class Person {
String name;
String city;
Person(String name, String city) {
this.name = name;
this.city = city;
}
String getCity() {
return city;
}
@Override
public String toString() {
return name + " (" + city + ")";
}
}
public class GroupingExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", "New York"),
new Person("Bob", "Los Angeles"),
new Person("Charlie", "New York"),
new Person("David", "Los Angeles")
);
// 按照 city 分组
Map<String, List<Person>> peopleByCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity));
// 输出分组结果
peopleByCity.forEach((city, list) -> {
System.out.println("City: " + city);
list.forEach(System.out::println);
});
}
}
输出:
City: New York
Alice (New York)
Charlie (New York)
City: Los Angeles
Bob (Los Angeles)
David (Los Angeles)
1.2 分组后统计数量
可以使用 Collectors.counting() 统计每个分组的元素数量。
Map<String, Long> countByCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity, Collectors.counting()));
countByCity.forEach((city, count) -> {
System.out.println("City: " + city + ", Count: " + count);
});
输出:
City: New York, Count: 2
City: Los Angeles, Count: 2
2. 多级分组
Collectors.groupingBy 支持多级分组,即按照多个条件进行分组。
2.1 示例
假设需要按照 city 和 name 的首字母进行分组。
Map<String, Map<Character, List<Person>>> peopleByCityAndInitial = people.stream()
.collect(Collectors.groupingBy(Person::getCity,
Collectors.groupingBy(p -> p.name.charAt(0))));
peopleByCityAndInitial.forEach((city, map) -> {
System.out.println("City: " + city);
map.forEach((initial, list) -> {
System.out.println(" Initial: " + initial);
list.forEach(System.out::println);
});
});
输出:
City: New York
Initial: A
Alice (New York)
Initial: C
Charlie (New York)
City: Los Angeles
Initial: B
Bob (Los Angeles)
Initial: D
David (Los Angeles)
3. 分组后对值进行操作
可以对分组后的值进行进一步操作,例如求和、求平均值等。
3.1 示例
假设有一个 List,需要按照 customer 分组,并计算每个客户的总订单金额。
import java.util.*;
import java.util.stream.Collectors;
class Order {
String customer;
double amount;
Order(String customer, double amount) {
this.customer = customer;
this.amount = amount;
}
String getCustomer() {
return customer;
}
double getAmount() {
return amount;
}
}
public class GroupingExample {
public static void main(String[] args) {
List<Order> orders = Arrays.asList(
new Order("Alice", 100.0),
new Order("Bob", 200.0),
new Order("Alice", 150.0),
new Order("Bob", 300.0)
);
// 按照 customer 分组,并计算总金额
Map<String, Double> totalAmountByCustomer = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.summingDouble(Order::getAmount)));
totalAmountByCustomer.forEach((customer, total) -> {
System.out.println("Customer: " + customer + ", Total Amount: " + total);
});
}
}
输出:
Customer: Alice, Total Amount: 250.0
Customer: Bob, Total Amount: 500.0
4. 自定义分组逻辑
可以通过自定义 Collector 实现更复杂的分组逻辑。
4.1 示例
假设需要将 List 按照 city 分组,并将每个城市的 Person 名称拼接成一个字符串。
Map<String, String> namesByCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity,
Collectors.mapping(Person::getName, Collectors.joining(", "))));
namesByCity.forEach((city, names) -> {
System.out.println("City: " + city + ", Names: " + names);
});
输出:
City: New York, Names: Alice, Charlie
City: Los Angeles, Names: Bob, David
5. 总结
Java 8 的 Stream API 和 Collectors.groupingBy 提供了强大的功能,可以轻松地对 List 进行分组操作。以下是本文的主要内容:
- 使用 Collectors.groupingBy 进行基本分组。
- 多级分组。
- 分组后对值进行操作(如统计数量、求和等)。
- 自定义分组逻辑。