averagingDouble
Collectors.averagingDouble() 是一个用于计算数值类型元素平均值的收集器,专门用于 Double 类型的数据。
基本用法
java
public static void main(String[] args) {
// 1. 基本用法 - 计算 Double 流的平均值
List<Double> numbers = Arrays.asList(1.5, 2.5, 3.5, 4.5, 5.5);
Double average = numbers.stream().collect(Collectors.averagingDouble(n -> n));
System.out.println("Average: " + average); // 3.5
// 2. 简化写法
Double average2 = numbers.stream()
.collect(Collectors.averagingDouble(Double::doubleValue));
// 3. 从 IntStream/LongStream 转换
double intAverage = IntStream.of(1, 2, 3, 4, 5)
.boxed()
.collect(Collectors.averagingDouble(Integer::doubleValue));
System.out.println("Int average: " + intAverage); // 3.0
}核心特性
1. 处理空流和 null 值
java
public static void main(String[] args) {
// 空流返回 0.0
Double emptyAverage = Stream.<Double>empty()
.collect(Collectors.averagingDouble(n -> n));
System.out.println("Empty stream average: " + emptyAverage); // 0.0
// 包含 null 值的流
List<Double> withNulls = Arrays.asList(1.0, null, 3.0, null, 5.0);
Double average = withNulls.stream()
.filter(Objects::nonNull) // 必须过滤 null
.collect(Collectors.averagingDouble(n -> n));
System.out.println("Average with nulls filtered: " + average); // 3.0
// 如果不过滤 null 会抛出 NullPointerException
// withNulls.stream().collect(Collectors.averagingDouble(n -> n));
}2. 精度处理
java
public static void main(String[] args) {
List<Double> numbers = Arrays.asList(1.0, 2.0, 3.0);
Double average = numbers.stream()
.collect(Collectors.averagingDouble(n -> n));
// 注意浮点数精度问题
System.out.println("Average: " + average); // 2.0
// 大数和小数混合的情况,可能会有精度损失
List<Double> mixed = Arrays.asList(1e10, 1e-10, 1e10);
Double mixedAverage = mixed.stream()
.collect(Collectors.averagingDouble(n -> n));
System.out.println("Mixed average: " + mixedAverage);
}实际应用示例
示例 1:对象属性平均值计算
java
class Student {
private String name;
private double score;
private int age;
public Student(String name, double score, int age) {
this.name = name;
this.score = score;
this.age = age;
}
// getters
public double getScore() { return score; }
public int getAge() { return age; }
public String getName() { return name; }
}
public class ObjectPropertyAverage {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 85.5, 20),
new Student("Bob", 92.0, 21),
new Student("Charlie", 78.5, 22),
new Student("Diana", 88.0, 20),
new Student("Eve", 95.5, 21)
);
// 计算平均分数
Double averageScore = students.stream()
.collect(Collectors.averagingDouble(Student::getScore));
System.out.println("Average score: " + averageScore); // 87.9
// 计算平均年龄(需要转换 int -> double)
Double averageAge = students.stream()
.collect(Collectors.averagingDouble(Student::getAge));
System.out.println("Average age: " + averageAge); // 20.8
// 使用 map 提取分数再计算
Double averageScore2 = students.stream()
.mapToDouble(Student::getScore)
.average()
.orElse(0.0);
System.out.println("Average score (alternative): " + averageScore2);
}
}示例 2:与 groupingBy() 结合使用
java
public class GroupingWithAverage {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", "Math", 85.5),
new Student("Bob", "Science", 92.0),
new Student("Charlie", "Math", 78.5),
new Student("Diana", "Science", 88.0),
new Student("Eve", "Math", 95.5),
new Student("Frank", "Science", 82.0)
);
// 按科目分组并计算每科平均分
Map<String, Double> avgScoreBySubject = students.stream()
.collect(Collectors.groupingBy(
Student::getSubject,
Collectors.averagingDouble(Student::getScore)
));
System.out.println("Average score by subject:");
avgScoreBySubject.forEach((subject, avg) ->
System.out.println(subject + ": " + avg));
// Math: 86.5, Science: 87.333...
// 更复杂的场景:按年级和科目双重分组
// 假设 Student 有 getGrade() 方法
Map<Integer, Map<String, Double>> nestedAvg = students.stream()
.collect(Collectors.groupingBy(
Student::getGrade,
Collectors.groupingBy(
Student::getSubject,
Collectors.averagingDouble(Student::getScore)
)
));
}
}示例 3:复杂数据聚合
java
class Order {
private String customerId;
private List<OrderItem> items;
private LocalDate orderDate;
public double getTotalAmount() {
return items.stream()
.mapToDouble(OrderItem::getPrice)
.sum();
}
// getters
}
class OrderItem {
private String productId;
private double price;
private int quantity;
public double getPrice() { return price * quantity; }
}
public class BusinessDataAnalysis {
public static void main(String[] args) {
List<Order> orders = getOrders();
// 计算每个客户的平均订单金额
Map<String, Double> avgOrderByCustomer = orders.stream()
.collect(Collectors.groupingBy(
Order::getCustomerId,
Collectors.averagingDouble(Order::getTotalAmount)
));
// 计算每月的平均订单金额
Map<YearMonth, Double> avgOrderByMonth = orders.stream()
.collect(Collectors.groupingBy(
order -> YearMonth.from(order.getOrderDate()),
Collectors.averagingDouble(Order::getTotalAmount)
));
// 计算每个产品的平均销售价格
// 需要 flatMap 展开订单项
Map<String, Double> avgPriceByProduct = orders.stream()
.flatMap(order -> order.getItems().stream())
.collect(Collectors.groupingBy(
OrderItem::getProductId,
Collectors.averagingDouble(OrderItem::getPrice)
));
}
}常见陷阱
陷阱 1:浮点数精度问题
java
public class FloatingPointPrecision {
public static void main(String[] args) {
// 浮点数计算可能有精度问题
List<Double> numbers = Arrays.asList(0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1);
Double average = numbers.stream()
.collect(Collectors.averagingDouble(n -> n));
System.out.println("Average of ten 0.1s: " + average);
// 理论上应该是 0.1,但实际上可能是 0.09999999999999999
// 解决方案:使用 BigDecimal 处理精度要求高的场景
BigDecimal exactAverage = numbers.stream()
.map(BigDecimal::valueOf)
.reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(numbers.size()), 2, RoundingMode.HALF_UP);
System.out.println("Exact average: " + exactAverage); // 0.10
}
}陷阱 2:空值和异常处理
java
public class NullHandlingBestPractice {
static class Product {
private Double price; // 可能为 null
public Product(Double price) {
this.price = price;
}
public Double getPrice() {
return price;
}
}
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product(100.0),
new Product(null), // 价格未知
new Product(200.0),
new Product(null)
);
// 最佳实践:明确处理 null 值
Double avgPrice = products.stream()
.filter(p -> p.getPrice() != null)
.collect(Collectors.averagingDouble(Product::getPrice));
System.out.println("Average price (excluding nulls): " + avgPrice); // 150.0
// 或者给 null 一个默认值
Double avgPriceWithDefault = products.stream()
.collect(Collectors.averagingDouble(p ->
p.getPrice() != null ? p.getPrice() : 0.0));
System.out.println("Average price (with default): " + avgPriceWithDefault); // 75.0
}
}
朔风