# Collector 高级用法 `Collectors`提供了丰富的收集器用于Stream的终止操作。 ## 基本收集器 ```java List<Employee> employees = getEmployees(); // toList/toSet List<String> names = employees.stream() .map(Employee::getName) .collect(Collectors.toList()); // toCollection - 指定集合类型 TreeSet<String> sortedNames = employees.stream() .map(Employee::getName) .collect(Collectors.toCollection(TreeSet::new)); ``` --- ## 统计收集器 ```java // counting long count = employees.stream() .collect(Collectors.counting()); // summingInt/summingDouble/summingLong int totalSalary = employees.stream() .collect(Collectors.summingInt(Employee::getSalary)); // averagingInt/averagingDouble/averagingLong double avgSalary = employees.stream() .collect(Collectors.averagingDouble(Employee::getSalary)); // summarizingInt - 获取统计摘要 IntSummaryStatistics stats = employees.stream() .collect(Collectors.summarizingInt(Employee::getSalary)); System.out.println("Max: " + stats.getMax()); System.out.println("Min: " + stats.getMin()); System.out.println("Avg: " + stats.getAverage()); ``` --- ## 分组 groupingBy ```java // 按部门分组 Map<String, List<Employee>> byDept = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment)); // 按条件分组 Map<Boolean, List<Employee>> byHighSalary = employees.stream() .collect(Collectors.partitioningBy(e -> e.getSalary() > 5000)); // 多级分组 Map<String, Map<Integer, List<Employee>>> byDeptAndAge = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.groupingBy(Employee::getAge) )); ``` --- ## groupingBy + 下游收集器 ```java // 按部门统计人数 Map<String, Long> countByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.counting() )); // 按部门计算平均工资 Map<String, Double> avgSalaryByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.averagingDouble(Employee::getSalary) )); // 按部门获取工资最高的员工 Map<String, Optional<Employee>> topByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.maxBy(Comparator.comparing(Employee::getSalary)) )); ``` --- ## 字符串连接 ```java // joining - 连接字符串 String names = employees.stream() .map(Employee::getName) .collect(Collectors.joining()); // AliceBobCharlie String namesWithDelimiter = employees.stream() .map(Employee::getName) .collect(Collectors.joining(", ")); // Alice, Bob, Charlie String formatted = employees.stream() .map(Employee::getName) .collect(Collectors.joining(", ", "Employees: [", "]")); // Employees: [Alice, Bob, Charlie] ``` --- ## toMap ```java // 转换为Map Map<Integer, String> idToName = employees.stream() .collect(Collectors.toMap( Employee::getId, // key Employee::getName // value )); // 处理重复key Map<String, Employee> nameToEmp = employees.stream() .collect(Collectors.toMap( Employee::getName, e -> e, (existing, replacement) -> existing // 保留现有的 )); // 指定Map类型 TreeMap<Integer, String> sortedMap = employees.stream() .collect(Collectors.toMap( Employee::getId, Employee::getName, (e1, e2) -> e1, TreeMap::new )); ``` --- ## 自定义Collector ```java // 使用Collector.of()创建自定义收集器 Collector<Employee, ?, String> employeeNameCollector = Collector.of( StringBuilder::new, // supplier (sb, emp) -> sb.append(emp.getName()).append(", "), // accumulator (sb1, sb2) -> sb1.append(sb2), // combiner StringBuilder::toString // finisher ); String allNames = employees.stream() .collect(employeeNameCollector); ```
---
# Stream调试技巧 ## 使用peek() ```java List<Integer> result = IntStream.range(0, 10) .peek(x -> System.out.println("原始值: " + x)) .filter(x -> x % 2 == 0) .peek(x -> System.out.println("过滤后: " + x)) .map(x -> x * 2) .peek(x -> System.out.println("映射后: " + x)) .boxed() .collect(Collectors.toList()); ``` **注意**:`peek()`是中间操作,只在有终止操作时才执行。 --- ## 设置断点 在IDE中可以在Lambda表达式内部设置断点: ```java employees.stream() .filter(e -> { // 在这里设置断点 return e.getSalary() > 5000; }) .map(e -> { // 或者这里 return e.getName(); }) .collect(Collectors.toList()); ``` --- ## 分步执行 ```java // 不好调试的写法 List<String> result = employees.stream() .filter(e -> e.getSalary() > 5000) .map(Employee::getName) .sorted() .collect(Collectors.toList()); // 分步执行便于调试 Stream<Employee> highEarners = employees.stream() .filter(e -> e.getSalary() > 5000); Stream<String> names = highEarners.map(Employee::getName); Stream<String> sorted = names.sorted(); List<String> result = sorted.collect(Collectors.toList()); ``` --- ## 打印Stream内容(调试用) ```java public static <T> void printStream(Stream<T> stream) { System.out.println( stream.collect(Collectors.toList()) ); } // 注意:Stream只能消费一次 Stream<Integer> stream = Stream.of(1, 2, 3); printStream(stream); // OK printStream(stream); // 错误!Stream已关闭 ``` --- # Lambda最佳实践总结 1. **保持Lambda简短**:一行最好,最多3-5行 2. **复杂逻辑提取为方法**:使用方法引用 3. **避免副作用**:不修改外部状态 4. **合理使用类型推断**:让代码更简洁 5. **优先使用方法引用**:更清晰、更简洁 6. **善用Stream API**:处理集合数据 7. **谨慎使用并行流**:不是万能的性能提升 8. **Optional处理null**:避免显式null检查 9. **组合小函数**:而不是写大Lambda 10. **异常处理要妥善**:不要吞没异常
---