Collectors.collectingAndThen()
Collectors.collectingAndThen() 是一个非常强大的收集器,它允许我们在完成收集操作后,对结果进行额外的转换操作。这是一个" 收集然后转换"的模式。
基本概念
java
// 方法签名
public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(
Collector<T, A, R> downstream, // 下游收集器
Function<R, RR> finisher // 完成后的转换函数
)基本用法
javas
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 1. 收集到列表后,转换为不可修改列表
List<String> unmodifiableList = names.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(), // 先收集到 List
Collections::unmodifiableList // 然后转换为不可修改列表
));
// unmodifiableList.add("Eve"); // ❌ 抛出 UnsupportedOperationException
// 2. 收集到集合后,获取大小
Integer setSize = names.stream()
.collect(Collectors.collectingAndThen(
Collectors.toSet(), // 先收集到 Set
Set::size // 然后获取大小
));
System.out.println("Unique names count: " + setSize); // 4
// 3. 收集到字符串后,转换为大写
String upperCaseNames = names.stream()
.collect(Collectors.collectingAndThen(
Collectors.joining(", "), // 先连接成字符串
String::toUpperCase // 然后转为大写
));
System.out.println("Names: " + upperCaseNames);
// ALICE, BOB, CHARLIE, DAVID核心特性
1. 转换收集结果
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算平均值后,格式化为字符串
String formattedAvg = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.averagingInt(n -> n),
avg -> String.format("Average: %.2f", avg)
));
System.out.println(formattedAvg); // Average: 3.00
// 收集到 Map 后,添加额外信息
Map<String, String> enhancedMap = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
n -> "num" + n,
n -> "value-" + n
),
map -> {
map.put("totalCount", String.valueOf(numbers.size()));
map.put("processedAt", LocalDateTime.now().toString());
return map;
}
));
System.out.println("Enhanced map: " + enhancedMap);2. 防御性编程 - 返回不可变集合
java
List<String> data = Arrays.asList("A", "B", "C", "A", "B");
// 返回不可修改的集合(防御性副本)
Set<String> unmodifiableSet = data.stream()
.collect(Collectors.collectingAndThen(
Collectors.toSet(),
Collections::unmodifiableSet
));
// 返回不可修改的排序集合
Set<String> unmodifiableSortedSet = data.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(TreeSet::new),
Collections::unmodifiableSortedSet
));
// 返回不可修改的列表(保持顺序)
List<String> unmodifiableList = data.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList
));
// 返回不可修改的 Map
Map<String, Integer> unmodifiableMap = data.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
str -> str,
str -> 1,
Integer::sum
),
Collections::unmodifiableMap
));
// 所有返回的集合都是不可修改的
// unmodifiableSet.add("D"); // ❌ UnsupportedOperationException实际应用示例
示例 1:数据统计与格式化
java
class Employee {
private String name;
private String department;
private double salary;
private int yearsOfExperience;
public Employee(String name, String department, double salary, int years) {
this.name = name;
this.department = department;
this.salary = salary;
this.yearsOfExperience = years;
}
// getters
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public int getYearsOfExperience() { return yearsOfExperience; }
}
public class EmployeeAnalysis {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", "Engineering", 85000, 3),
new Employee("Bob", "Engineering", 95000, 5),
new Employee("Charlie", "Sales", 70000, 2),
new Employee("Diana", "Sales", 75000, 4),
new Employee("Eve", "HR", 60000, 1)
);
// 1. 按部门分组,统计平均薪资并格式化
Map<String, String> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.collectingAndThen(
Collectors.averagingDouble(Employee::getSalary),
avg -> String.format("$%,.2f", avg)
)
));
System.out.println("Average salary by department:");
avgSalaryByDept.forEach((dept, avg) ->
System.out.printf("%s: %s%n", dept, avg));
// Engineering: $90,000.00
// Sales: $72,500.00
// HR: $60,000.00
// 2. 统计每个部门员工数量,并添加标签
Map<String, String> employeeCountByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.collectingAndThen(
Collectors.counting(),
count -> String.format("%d %s",
count, count == 1 ? "employee" : "employees")
)
));
System.out.println("\nEmployee count by department:");
employeeCountByDept.forEach((dept, count) ->
System.out.printf("%s: %s%n", dept, count));
// 3. 获取最高薪水的员工信息
String highestPaidInfo = employees.stream()
.collect(Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)),
opt -> opt.map(emp -> String.format(
"%s earns $%,.2f in %s department",
emp.getName(), emp.getSalary(), emp.getDepartment()
)).orElse("No employees found")
));
System.out.println("\nHighest paid employee: " + highestPaidInfo);
// 4. 综合统计:部门薪资报告
Map<String, Map<String, Object>> departmentReport = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.collectingAndThen(
Collectors.summarizingDouble(Employee::getSalary),
stats -> Map.of(
"count", stats.getCount(),
"avgSalary", String.format("$%,.2f", stats.getAverage()),
"totalSalary", String.format("$%,.2f", stats.getSum()),
"minSalary", String.format("$%,.2f", stats.getMin()),
"maxSalary", String.format("$%,.2f", stats.getMax())
)
)
));
System.out.println("\nDepartment salary report:");
departmentReport.forEach((dept, stats) -> {
System.out.println(dept + ":");
stats.forEach((key, value) ->
System.out.printf(" %s: %s%n", key, value));
});
}
}示例 3:订单处理系统
java
class Order {
private String orderId;
private String customerId;
private List<OrderItem> items;
private LocalDateTime orderDate;
private String status; // "PENDING", "PROCESSING", "SHIPPED", "DELIVERED"
public Order(String orderId, String customerId, List<OrderItem> items) {
this.orderId = orderId;
this.customerId = customerId;
this.items = items;
this.orderDate = LocalDateTime.now();
this.status = "PENDING";
}
// getters
public String getOrderId() { return orderId; }
public String getCustomerId() { return customerId; }
public List<OrderItem> getItems() { return items; }
public LocalDateTime getOrderDate() { return orderDate; }
public String getStatus() { return status; }
public double getTotalAmount() {
return items.stream()
.mapToDouble(OrderItem::getTotalPrice)
.sum();
}
public void setStatus(String status) {
this.status = status;
}
}
class OrderItem {
private String productId;
private String productName;
private double unitPrice;
private int quantity;
public OrderItem(String productId, String productName, double unitPrice, int quantity) {
this.productId = productId;
this.productName = productName;
this.unitPrice = unitPrice;
this.quantity = quantity;
}
// getters
public String getProductId() { return productId; }
public String getProductName() { return productName; }
public double getUnitPrice() { return unitPrice; }
public int getQuantity() { return quantity; }
public double getTotalPrice() {
return unitPrice * quantity;
}
}
public class OrderProcessing {
public static void main(String[] args) {
List<Order> orders = Arrays.asList(
new Order("ORD001", "CUST001", Arrays.asList(
new OrderItem("P001", "Laptop", 1200.0, 1),
new OrderItem("P002", "Mouse", 25.0, 2)
)),
new Order("ORD002", "CUST002", Arrays.asList(
new OrderItem("P003", "Keyboard", 80.0, 1),
new OrderItem("P004", "Monitor", 300.0, 1)
)),
new Order("ORD003", "CUST001", Arrays.asList(
new OrderItem("P002", "Mouse", 25.0, 5),
new OrderItem("P005", "USB Cable", 10.0, 3)
)),
new Order("ORD004", "CUST003", Arrays.asList(
new OrderItem("P001", "Laptop", 1200.0, 2),
new OrderItem("P006", "Headphones", 150.0, 1)
))
);
// 1. 按客户分组,计算总消费金额并格式化
Map<String, String> customerSpending = orders.stream()
.collect(Collectors.groupingBy(
Order::getCustomerId,
Collectors.collectingAndThen(
Collectors.summingDouble(Order::getTotalAmount),
total -> String.format("$%,.2f", total)
)
));
System.out.println("Customer total spending:");
customerSpending.forEach((customerId, total) ->
System.out.printf("%s: %s%n", customerId, total));
// 2. 热门产品分析
Map<String, Integer> productSales = orders.stream()
.flatMap(order -> order.getItems().stream())
.collect(Collectors.groupingBy(
OrderItem::getProductId,
Collectors.collectingAndThen(
Collectors.summingInt(OrderItem::getQuantity),
quantity -> quantity // 可以直接返回,这里演示转换
)
));
System.out.println("\nProduct sales quantity:");
productSales.forEach((productId, quantity) ->
System.out.printf("%s: %d units%n", productId, quantity));
// 3. 订单处理流水线
List<Order> processedOrders = orders.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
orderList -> {
// 第一阶段:标记为处理中
orderList.forEach(order -> order.setStatus("PROCESSING"));
// 第二阶段:过滤出总金额大于500的订单
List<Order> highValueOrders = orderList.stream()
.filter(order -> order.getTotalAmount() > 500.0)
.collect(Collectors.toList());
// 第三阶段:标记高价值订单为优先处理
highValueOrders.forEach(order ->
System.out.printf("优先处理订单: %s (金额: $%,.2f)%n",
order.getOrderId(), order.getTotalAmount()));
return highValueOrders;
}
));
System.out.println("\n高价值订单数量: " + processedOrders.size());
// 4. 生成订单汇总报告
String orderSummary = orders.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
orderList -> {
double totalRevenue = orderList.stream()
.mapToDouble(Order::getTotalAmount)
.sum();
long totalItems = orderList.stream()
.flatMap(order -> order.getItems().stream())
.mapToLong(OrderItem::getQuantity)
.sum();
Map<String, Long> customerOrderCount = orderList.stream()
.collect(Collectors.groupingBy(
Order::getCustomerId,
Collectors.counting()
));
StringBuilder report = new StringBuilder();
report.append("========== 订单汇总报告 ==========\n");
report.append("生成时间: ").append(LocalDateTime.now()).append("\n");
report.append("总订单数: ").append(orderList.size()).append("\n");
report.append("总销售额: $").append(String.format("%,.2f", totalRevenue)).append("\n");
report.append("总商品数: ").append(totalItems).append("\n");
report.append("\n按客户统计:\n");
customerOrderCount.forEach((customerId, count) ->
report.append(String.format(" 客户 %s: %d 个订单%n", customerId, count)));
return report.toString();
}
));
System.out.println("\n" + orderSummary);
// 5. 批量更新订单状态
boolean allUpdated = orders.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
orderList -> {
// 模拟批量更新操作
System.out.println("\n开始批量更新订单状态...");
orderList.forEach(order -> {
// 根据订单金额设置不同状态
if (order.getTotalAmount() > 1000.0) {
order.setStatus("EXPEDITED_SHIPPING");
} else {
order.setStatus("STANDARD_SHIPPING");
}
System.out.printf("订单 %s 状态更新为: %s%n",
order.getOrderId(), order.getStatus());
});
// 验证所有订单都已更新
return orderList.stream()
.allMatch(order -> !"PENDING".equals(order.getStatus()));
}
));
System.out.println("\n所有订单状态更新完成: " + allUpdated);
}
}示例 3:链式转换
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 链式转换:收集 -> 过滤 -> 转换 -> 返回
List<Integer> result = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.filter(n -> n % 2 == 0) // 只保留偶数
.map(n -> n * 2) // 乘以2
.sorted(Comparator.reverseOrder()) // 倒序排序
.collect(Collectors.toList())
));
System.out.println("Chained result: " + result); // [20, 16, 12, 8, 4]
// 更复杂的链式操作
String analysis = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.summarizingInt(n -> n),
stats -> {
// 多个转换步骤
double avg = stats.getAverage();
long sum = stats.getSum();
long count = stats.getCount();
// 分析逻辑
String analysisResult;
if (avg > 5) {
analysisResult = "高于平均值";
} else if (avg < 5) {
analysisResult = "低于平均值";
} else {
analysisResult = "等于平均值";
}
// 格式化输出
return String.format(
"总数: %d, 总和: %d, 平均: %.2f (%s)",
count, sum, avg, analysisResult
);
}
));
System.out.println("Analysis: " + analysis);
朔风