Skip to content
章节导航

averagingInt

Collectors.averagingInt() 是专门用于计算整数(int)类型数据平均值的收集器,返回结果为 Double 类型。

基本用法

java
// 1. 基本用法 - 计算整数列表的平均值
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

Double average = numbers.stream()
        .collect(Collectors.averagingInt(n -> n));
        System.out.println("Average: " + average); // 3.0

// 2. 方法引用简化写法
Double average2 = numbers.stream()
        .collect(Collectors.averagingInt(Integer::intValue));

// 3. 从 IntStream 直接使用
double intStreamAverage = IntStream.of(1, 2, 3, 4, 5)
        .average()
        .orElse(0.0);
        System.out.println("IntStream average: " + intStreamAverage); // 3.0

核心特性

1. 自动 int 到 double 转换

java
public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // averagingInt 内部会自动将 int 转换为 double 进行计算
        Double average = numbers.stream().collect(Collectors.averagingInt(n -> n));
        
        // 计算过程:(1 + 2 + 3 + 4 + 5) / 5 = 3.0
        System.out.println("Average: " + average); // 3.0
        System.out.println("Type: " + average.getClass()); // java.lang.Double
    }

2. 空流处理

java
// 空流返回 0.0(不是 0)
Double emptyAverage = Stream.<Integer>empty()
        .collect(Collectors.averagingInt(n -> n));
System.out.println("Empty stream average: " + emptyAverage); // 0.0
System.out.println("Is zero? " + (emptyAverage == 0.0)); // true

// 包含 null 值的流(需要过滤)
List<Integer> withNulls = Arrays.asList(1, null, 3, null, 5);
Double average = withNulls.stream()
        .filter(Objects::nonNull)
        .collect(Collectors.averagingInt(n -> n));
System.out.println("Average with nulls filtered: " + average); // 3.0

实际应用示例

示例 1:对象属性平均值计算

java
class Employee {
    private String name;
    private int age;
    private int salary;  // 月薪
    private int yearsOfService;
    
    public Employee(String name, int age, int salary, int yearsOfService) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.yearsOfService = yearsOfService;
    }
    
    // getters
    public int getAge() { return age; }
    public int getSalary() { return salary; }
    public int getYearsOfService() { return yearsOfService; }
    public String getName() { return name; }
}

public class EmployeeAnalysis {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", 25, 50000, 3),
            new Employee("Bob", 30, 60000, 5),
            new Employee("Charlie", 35, 70000, 8),
            new Employee("Diana", 28, 55000, 4),
            new Employee("Eve", 40, 80000, 12)
        );
        
        // 计算平均年龄
        Double averageAge = employees.stream()
            .collect(Collectors.averagingInt(Employee::getAge));
        
        System.out.println("Average age: " + averageAge); // 31.6
        
        // 计算平均薪资
        Double averageSalary = employees.stream()
            .collect(Collectors.averagingInt(Employee::getSalary));
        
        System.out.println("Average salary: " + averageSalary); // 63000.0
        
        // 计算平均工作年限
        Double averageServiceYears = employees.stream()
            .collect(Collectors.averagingInt(Employee::getYearsOfService));
        
        System.out.println("Average years of service: " + averageServiceYears); // 6.4
        
        // 使用 mapToInt 的替代方式
        double avgAgeAlternative = employees.stream()
            .mapToInt(Employee::getAge)
            .average()
            .orElse(0.0);
        
        System.out.println("Alternative average age: " + avgAgeAlternative);
    }
}

示例 2:与 groupingBy() 结合使用

java
class Product {
    private String category;
    private int price;  // 价格(分或元)
    private int stock;
    private String supplier;
    
    public Product(String category, int price, int stock, String supplier) {
        this.category = category;
        this.price = price;
        this.stock = stock;
        this.supplier = supplier;
    }
    
    // getters
    public String getCategory() { return category; }
    public int getPrice() { return price; }
    public int getStock() { return stock; }
    public String getSupplier() { return supplier; }
}

public class ProductAnalysis {
    public static void main(String[] args) {
        List<Product> products = Arrays.asList(
            new Product("Electronics", 99900, 50, "SupplierA"),
            new Product("Electronics", 149900, 30, "SupplierB"),
            new Product("Clothing", 29900, 100, "SupplierA"),
            new Product("Clothing", 49900, 80, "SupplierC"),
            new Product("Books", 1990, 200, "SupplierB"),
            new Product("Books", 2990, 150, "SupplierC"),
            new Product("Electronics", 79900, 60, "SupplierC")
        );
        
        // 1. 按类别分组计算平均价格
        Map<String, Double> avgPriceByCategory = products.stream()
            .collect(Collectors.groupingBy(
                Product::getCategory,
                Collectors.averagingInt(Product::getPrice)
            ));
        
        System.out.println("Average price by category:");
        avgPriceByCategory.forEach((category, avgPrice) -> 
            System.out.printf("%s: %.2f%n", category, avgPrice));
        // Electronics: 109866.67
        // Clothing: 39900.00
        // Books: 2490.00
        
        // 2. 按供应商分组计算平均库存
        Map<String, Double> avgStockBySupplier = products.stream()
            .collect(Collectors.groupingBy(
                Product::getSupplier,
                Collectors.averagingInt(Product::getStock)
            ));
        
        System.out.println("\nAverage stock by supplier:");
        avgStockBySupplier.forEach((supplier, avgStock) -> 
            System.out.printf("%s: %.1f%n", supplier, avgStock));
        
        // 3. 按类别和供应商双重分组计算平均价格
        Map<String, Map<String, Double>> nestedAvg = products.stream()
            .collect(Collectors.groupingBy(
                Product::getCategory,
                Collectors.groupingBy(
                    Product::getSupplier,
                    Collectors.averagingInt(Product::getPrice)
                )
            ));
        
        System.out.println("\nNested average prices:");
        nestedAvg.forEach((category, supplierMap) -> {
            System.out.println(category + ":");
            supplierMap.forEach((supplier, avgPrice) -> 
                System.out.printf("  %s: %.2f%n", supplier, avgPrice));
        });
    }
}

示例 3:分数统计系统

java
class ExamScore {
    private String studentId;
    private String subject;
    private int score;  // 0-100 分
    private int examYear;
    
    public ExamScore(String studentId, String subject, int score, int examYear) {
        this.studentId = studentId;
        this.subject = subject;
        this.score = score;
        this.examYear = examYear;
    }
    
    // getters
    public String getStudentId() { return studentId; }
    public String getSubject() { return subject; }
    public int getScore() { return score; }
    public int getExamYear() { return examYear; }
}

public class ExamScoreAnalysis {
    public static void main(String[] args) {
        List<ExamScore> scores = Arrays.asList(
            new ExamScore("S001", "Math", 85, 2023),
            new ExamScore("S002", "Math", 92, 2023),
            new ExamScore("S001", "English", 78, 2023),
            new ExamScore("S002", "English", 88, 2023),
            new ExamScore("S003", "Math", 76, 2023),
            new ExamScore("S003", "English", 82, 2023),
            new ExamScore("S001", "Math", 90, 2024),
            new ExamScore("S002", "Math", 95, 2024),
            new ExamScore("S001", "English", 85, 2024)
        );
        
        // 1. 各科目平均分
        Map<String, Double> avgScoreBySubject = scores.stream()
            .collect(Collectors.groupingBy(
                ExamScore::getSubject,
                Collectors.averagingInt(ExamScore::getScore)
            ));
        
        System.out.println("Average score by subject:");
        avgScoreBySubject.forEach((subject, avg) -> 
            System.out.printf("%s: %.2f%n", subject, avg));
        
        // 2. 每年各科目平均分
        Map<Integer, Map<String, Double>> yearlyAvg = scores.stream()
            .collect(Collectors.groupingBy(
                ExamScore::getExamYear,
                Collectors.groupingBy(
                    ExamScore::getSubject,
                    Collectors.averagingInt(ExamScore::getScore)
                )
            ));
        
        System.out.println("\nYearly average scores by subject:");
        yearlyAvg.forEach((year, subjectMap) -> {
            System.out.println(year + ":");
            subjectMap.forEach((subject, avg) -> 
                System.out.printf("  %s: %.2f%n", subject, avg));
        });
        
        // 3. 每个学生的平均分
        Map<String, Double> avgScoreByStudent = scores.stream()
            .collect(Collectors.groupingBy(
                ExamScore::getStudentId,
                Collectors.averagingInt(ExamScore::getScore)
            ));
        
        System.out.println("\nAverage score by student:");
        avgScoreByStudent.forEach((studentId, avg) -> 
            System.out.printf("%s: %.2f%n", studentId, avg));
        
        // 4. 找出高于平均分的学生
        Double overallAverage = scores.stream()
            .collect(Collectors.averagingInt(ExamScore::getScore));
        
        System.out.println("\nOverall average: " + overallAverage);
        
        Map<String, List<ExamScore>> aboveAverage = scores.stream()
            .filter(score -> score.getScore() > overallAverage)
            .collect(Collectors.groupingBy(ExamScore::getStudentId));
        
        System.out.println("Students with scores above average:");
        aboveAverage.forEach((studentId, studentScores) -> {
            System.out.println(studentId + ": " + 
                studentScores.stream()
                    .map(ExamScore::getScore)
                    .map(String::valueOf)
                    .collect(Collectors.joining(", ")));
        });
    }
}

常见陷阱

陷阱 1:整数除法问题

java
List<Integer> numbers = Arrays.asList(1, 2, 3);

// ❌ 错误的做法:直接使用整数除法
double wrongAvg = numbers.stream()
        .reduce(0, Integer::sum) / numbers.size(); // 结果是 2.0,不是 2.666...
        System.out.println("Wrong average: " + wrongAvg); // 2.0

// ✅ 正确的做法:使用 averagingInt 或转换为 double
double correctAvg1 = numbers.stream()
        .collect(Collectors.averagingInt(n -> n)); // 2.666...

double correctAvg2 = numbers.stream()
        .mapToInt(Integer::intValue)
        .average()
        .orElse(0.0); // 2.666...

System.out.println("Correct average 1: " + correctAvg1);
System.out.println("Correct average 2: " + correctAvg2);

陷阱 2:大数据集的精度问题

java
// 大整数相加可能溢出
List<Integer> largeNumbers = Arrays.asList(
        Integer.MAX_VALUE - 100,
        Integer.MAX_VALUE - 200,
        Integer.MAX_VALUE - 300
);

// averagingInt 内部使用 long 进行累加,避免溢出
Double average = largeNumbers.stream()
        .collect(Collectors.averagingInt(n -> n));
System.out.println("Average of large numbers: " + average);

// 如果自己实现要小心溢出
// ❌ 可能溢出
// int sum = largeNumbers.stream().reduce(0, Integer::sum);

// ✅ 使用 long 避免溢出
long safeSum = largeNumbers.stream()
        .mapToLong(Integer::longValue)
        .sum();

double safeAvg = (double) safeSum / largeNumbers.size();
System.out.println("Safe average: " + safeAvg);

最佳实践:防御性编程

java
public class DefensiveProgramming {
    static class User {
        private String name;
        private Integer age;  // 可能为 null
        
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        
        public Integer getAge() {
            return age;
        }
    }
    
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Alice", 25),
            new User("Bob", null),    // 年龄未知
            new User("Charlie", 30),
            new User("Diana", null)
        );
        
        // 最佳实践1:明确处理 null 值
        Double avgAgeWithFilter = users.stream()
            .filter(user -> user.getAge() != null)
            .collect(Collectors.averagingInt(User::getAge));
        
        System.out.println("Average age (excluding nulls): " + avgAgeWithFilter); // 27.5
        
        // 最佳实践2:提供默认值
        Double avgAgeWithDefault = users.stream()
            .collect(Collectors.averagingInt(user -> 
                user.getAge() != null ? user.getAge() : 0));
        
        System.out.println("Average age (with default 0): " + avgAgeWithDefault); // 13.75
        
        // 最佳实践3:使用 Optional 包装结果
        OptionalDouble optionalAvg = users.stream()
            .filter(user -> user.getAge() != null)
            .mapToInt(User::getAge)
            .average();
        
        optionalAvg.ifPresentOrElse(
            avg -> System.out.println("Optional average: " + avg),
            () -> System.out.println("No valid ages found")
        );
    }
}