Java 收集器工厂方法使用案例
以下是 Collectors 类中常用工厂方法的详细使用案例:
1. toList() - 收集到列表
import java.util.*;
import java.util.stream.*;
public class CollectorExamples {
public static void main(String[] args) {
// 示例数据
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Alice");
// toList() - 收集到列表
List<String> nameList = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
System.out.println("toList 示例: " + nameList);
// 输出: [Alice, Charlie, David, Alice] (保留重复元素)
// 处理数值类型
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println("数值平方列表: " + squares);
// 输出: [1, 4, 9, 16, 25]
}
}
2. toSet() - 收集到集合(去重)
import java.util.*;
import java.util.stream.*;
public class CollectorExamples {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Alice");
// toSet() - 自动去重
Set<String> nameSet = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toSet());
System.out.println("toSet 示例: " + nameSet);
// 输出: [Alice, Charlie, David] (自动去重,顺序可能不同)
// 自定义对象示例
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 6000),
new Employee("Alice", "IT", 5500),
new Employee("Charlie", "HR", 5200)
);
// 根据姓名去重收集
Set<String> uniqueNames = employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
System.out.println("员工姓名集合: " + uniqueNames);
// 输出: [Alice, Bob, Charlie]
}
static class Employee {
private String name;
private String department;
private double salary;
public Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
}
}
3. toMap() - 收集到映射
import java.util.*;
import java.util.stream.*;
public class CollectorExamples {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 6000),
new Employee("Charlie", "HR", 5200),
new Employee("David", "IT", 6500)
);
// 简单的 toMap - 姓名作为键,员工对象作为值
Map<String, Employee> employeeMap = employees.stream()
.collect(Collectors.toMap(
Employee::getName, // 键提取器
employee -> employee // 值提取器
));
System.out.println("员工映射: " +
employeeMap.keySet().stream()
.map(key -> key + "=" + employeeMap.get(key).getDepartment())
.collect(Collectors.joining(", ")));
// 输出类似: Alice=HR, Bob=IT, Charlie=HR, David=IT
// 处理键冲突的情况
List<Employee> employeesWithDuplicates = Arrays.asList(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 6000),
new Employee("Alice", "Finance", 5500) // 重复的键
);
Map<String, Employee> mergedMap = employeesWithDuplicates.stream()
.collect(Collectors.toMap(
Employee::getName,
employee -> employee,
(existing, replacement) -> {
// 处理键冲突:保留工资更高的员工
return existing.getSalary() >= replacement.getSalary()
? existing : replacement;
}
));
System.out.println("处理冲突后的映射: " +
mergedMap.entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue().getDepartment())
.collect(Collectors.joining(", ")));
// 输出: Alice=Finance, Bob=IT
// 指定特定的 Map 实现
Map<String, Double> salaryMap = employees.stream()
.collect(Collectors.toMap(
Employee::getName,
Employee::getSalary,
(v1, v2) -> v1, // 如果键重复,保留第一个值
TreeMap::new // 使用 TreeMap 保持排序
));
System.out.println("排序的薪资映射: " + salaryMap);
// 输出: {Alice=5000.0, Bob=6000.0, Charlie=5200.0, David=6500.0}
}
}
4. groupingBy() - 分组收集
import java.util.*;
import java.util.stream.*;
public class CollectorExamples {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 6000),
new Employee("Charlie", "HR", 5200),
new Employee("David", "IT", 6500),
new Employee("Eve", "Finance", 7000),
new Employee("Frank", "Finance", 5500)
);
// 简单分组 - 按部门分组
Map<String, List<Employee>> employeesByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
System.out.println("按部门分组:");
employeesByDept.forEach((dept, empList) ->
System.out.println(dept + ": " +
empList.stream()
.map(Employee::getName)
.collect(Collectors.joining(", ")))
);
// 输出:
// HR: Alice, Charlie
// Finance: Eve, Frank
// IT: Bob, David
// 分组后对值进行进一步处理
Map<String, Long> deptCount = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting() // 统计每个部门的人数
));
System.out.println("各部门人数: " + deptCount);
// 输出: {HR=2, Finance=2, IT=2}
// 分组后计算平均薪资
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
System.out.println("各部门平均薪资: " + avgSalaryByDept);
// 输出类似: {HR=5100.0, Finance=6250.0, IT=6250.0}
// 多级分组 - 先按部门,再按薪资级别
Map<String, Map<String, List<Employee>>> multiLevelGrouping = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(emp ->
emp.getSalary() >= 6000 ? "高薪" : "普通"
)
));
System.out.println("\n多级分组结果:");
multiLevelGrouping.forEach((dept, salaryGroup) -> {
System.out.println("部门: " + dept);
salaryGroup.forEach((level, empList) ->
System.out.println(" " + level + ": " +
empList.stream()
.map(Employee::getName)
.collect(Collectors.joining(", ")))
);
});
// 输出:
// 部门: HR
// 普通: Alice, Charlie
// 部门: Finance
// 高薪: Eve
// 普通: Frank
// 部门: IT
// 高薪: Bob, David
// 使用特定 Map 实现的分组
Map<String, Set<String>> namesByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
TreeMap::new, // 使用 TreeMap 保持部门名称排序
Collectors.mapping(
Employee::getName,
Collectors.toSet() // 收集为 Set 去重
)
));
System.out.println("\n排序的部门-姓名映射: " + namesByDept);
// 输出类似: {Finance=[Eve, Frank], HR=[Alice, Charlie], IT=[Bob, David]}
}
}
5. joining() - 连接字符串
import java.util.*;
import java.util.stream.*;
public class CollectorExamples {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "Date");
// 简单连接
String simpleJoin = fruits.stream()
.collect(Collectors.joining());
System.out.println("简单连接: " + simpleJoin);
// 输出: AppleBananaCherryDate
// 使用分隔符连接
String withDelimiter = fruits.stream()
.collect(Collectors.joining(", "));
System.out.println("带分隔符连接: " + withDelimiter);
// 输出: Apple, Banana, Cherry, Date
// 使用分隔符、前缀和后缀
String withPrefixSuffix = fruits.stream()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println("带前缀后缀连接: " + withPrefixSuffix);
// 输出: [Apple, Banana, Cherry, Date]
// 结合其他操作
String filteredJoin = fruits.stream()
.filter(fruit -> fruit.length() > 5)
.map(String::toUpperCase)
.collect(Collectors.joining(" | ", "Fruits: ", " (end)"));
System.out.println("过滤转换后连接: " + filteredJoin);
// 输出: Fruits: BANANA | CHERRY (end)
// 处理对象列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 6000),
new Employee("Charlie", "HR", 5200)
);
String employeeNames = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(", ", "Employees: ", ""));
System.out.println("员工姓名连接: " + employeeNames);
// 输出: Employees: Alice, Bob, Charlie
// 复杂连接示例
String complexJoin = employees.stream()
.map(emp -> emp.getName() + "(" + emp.getDepartment() + ")")
.collect(Collectors.joining("; ", "[", "]"));
System.out.println("复杂格式连接: " + complexJoin);
// 输出: [Alice(HR); Bob(IT); Charlie(HR)]
}
}
6. 综合示例
import java.util.*;
import java.util.stream.*;
public class CollectorExamples {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 6000),
new Employee("Charlie", "HR", 5200),
new Employee("David", "IT", 6500),
new Employee("Eve", "Finance", 7000),
new Employee("Frank", "Finance", 5500),
new Employee("Grace", "IT", 6200)
);
// 综合应用:分析员工数据
Map<String, String> departmentSummary = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.collectingAndThen(
Collectors.toList(),
list -> {
long count = list.size();
double avgSalary = list.stream()
.mapToDouble(Employee::getSalary)
.average()
.orElse(0.0);
String names = list.stream()
.map(Employee::getName)
.sorted()
.collect(Collectors.joining(", "));
return String.format("人数: %d, 平均薪资: %.2f, 员工: [%s]",
count, avgSalary, names);
}
)
));
System.out.println("部门综合分析:");
departmentSummary.forEach((dept, summary) ->
System.out.println(dept + " -> " + summary)
);
// 输出示例:
// HR -> 人数: 2, 平均薪资: 5100.00, 员工: [Alice, Charlie]
// Finance -> 人数: 2, 平均薪资: 6250.00, 员工: [Eve, Frank]
// IT -> 人数: 3, 平均薪资: 6233.33, 员工: [Bob, David, Grace]
}
}
7. 其他常用收集器
import java.util.*;
import java.util.stream.*;
import java.util.IntSummaryStatistics;
public class CollectorExamples {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 统计信息
IntSummaryStatistics stats = numbers.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println("统计信息: " + stats);
System.out.println("平均值: " + stats.getAverage());
System.out.println("总数: " + stats.getSum());
System.out.println("最大值: " + stats.getMax());
System.out.println("最小值: " + stats.getMin());
System.out.println("数量: " + stats.getCount());
// 分区(分为 true 和 false 两组)
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("奇偶数分区: " + partitioned);
// 输出: {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
// 归约操作
Optional<Integer> sum = numbers.stream()
.collect(Collectors.reducing((a, b) -> a + b));
System.out.println("总和: " + sum.orElse(0));
// 映射后再收集
List<String> numberStrings = numbers.stream()
.collect(Collectors.mapping(
n -> "Number: " + n,
Collectors.toList()
));
System.out.println("映射后收集: " + numberStrings);
}
}
总结
| 收集器 | 用途 | 特点 |
|---|---|---|
toList() |
收集到列表 | 保留顺序,允许重复 |
toSet() |
收集到集合 | 自动去重,不保证顺序 |
toMap() |
收集到映射 | 需要键值提取器,可处理键冲突 |
groupingBy() |
分组收集 | 强大的分组功能,支持多级分组 |
joining() |
连接字符串 | 灵活配置分隔符、前缀、后缀 |
这些收集器可以组合使用,创建复杂的数据处理管道,是Java Stream API中非常强大的工具。

已有 0 条评论