在日常开发中,我们经常会遇到需要计算集合中的组内平均值的情景。无论是学生成绩的平均分,还是商品价格的平均值,分组计算平均值都是一个普遍的需求。本文将介绍如何使用Java集合来计算组内平均值,并提供多个实际代码案例。

1. 基本概念

首先,我们需要了解什么是组内平均值。组内平均值是指在一个集合中,将具有相同特征(如相同的组、类别等)的元素分组,然后对每一组的元素进行平均值计算。

2. 使用简单的Java集合计算组内平均值

我们首先从最简单的情况开始,假设我们有一个学生成绩的列表,并且需要根据班级计算每个班级的平均成绩。

案例1:简单的分组平均值计算

假设我们有一个Student类:

class Student {
    String name;
    String className;  // 班级名称
    int score;         // 成绩

    public Student(String name, String className, int score) {
        this.name = name;
        this.className = className;
        this.score = score;
    }

    public String getClassName() {
        return className;
    }

    public int getScore() {
        return score;
    }
}

我们可以使用Java集合来计算每个班级的平均成绩:

import java.util.*;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", "ClassA", 85),
            new Student("Bob", "ClassA", 90),
            new Student("Charlie", "ClassB", 78),
            new Student("David", "ClassB", 88),
            new Student("Eve", "ClassA", 92)
        );

        Map<String, Double> averageScores = students.stream()
            .collect(Collectors.groupingBy(
                Student::getClassName,
                Collectors.averagingInt(Student::getScore)
            ));

        averageScores.forEach((className, avgScore) -> {
            System.out.println("Class: " + className + ", Average Score: " + avgScore);
        });
    }
}

输出结果:

Class: ClassA, Average Score: 89.0
Class: ClassB, Average Score: 83.0

在这个示例中,我们使用Java的Stream API和Collectors.groupingBy来对学生进行分组,然后使用Collectors.averagingInt来计算每个班级的平均成绩。

3. 使用复杂数据结构计算组内平均值

在实际开发中,数据往往更加复杂,可能包含嵌套结构。下面我们将演示如何处理更加复杂的场景。

案例2:基于嵌套结构的分组平均值计算

假设我们有一个更复杂的Student类,包含一个嵌套的Score类:

class Score {
    String subject;
    int score;

    public Score(String subject, int score) {
        this.subject = subject;
        this.score = score;
    }

    public String getSubject() {
        return subject;
    }

    public int getScore() {
        return score;
    }
}

class Student {
    String name;
    String className;
    List<Score> scores;

    public Student(String name, String className, List<Score> scores) {
        this.name = name;
        this.className = className;
        this.scores = scores;
    }

    public String getClassName() {
        return className;
    }

    public List<Score> getScores() {
        return scores;
    }
}

我们可以使用Java集合和Stream API来计算每个班级的每门课程的平均成绩:

import java.util.*;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", "ClassA", Arrays.asList(new Score("Math", 85), new Score("English", 90))),
            new Student("Bob", "ClassA", Arrays.asList(new Score("Math", 90), new Score("English", 88))),
            new Student("Charlie", "ClassB", Arrays.asList(new Score("Math", 78), new Score("English", 82))),
            new Student("David", "ClassB", Arrays.asList(new Score("Math", 88), new Score("English", 85))),
            new Student("Eve", "ClassA", Arrays.asList(new Score("Math", 92), new Score("English", 87)))
        );

        Map<String, Map<String, Double>> classSubjectAverageScores = students.stream()
            .flatMap(student -> student.getScores().stream()
                .map(score -> new AbstractMap.SimpleEntry<>(
                    new AbstractMap.SimpleEntry<>(student.getClassName(), score.getSubject()), 
                    score.getScore())))
            .collect(Collectors.groupingBy(
                entry -> entry.getKey().getKey(), // className
                Collectors.groupingBy(
                    entry -> entry.getKey().getValue(), // subject
                    Collectors.averagingInt(Map.Entry::getValue)
                )
            ));

        classSubjectAverageScores.forEach((className, subjectAverages) -> {
            System.out.println("Class: " + className);
            subjectAverages.forEach((subject, avgScore) -> {
                System.out.println("  Subject: " + subject + ", Average Score: " + avgScore);
            });
        });
    }
}

输出结果:

Class: ClassA
  Subject: Math, Average Score: 89.0
  Subject: English, Average Score: 88.33333333333333
Class: ClassB
  Subject: Math, Average Score: 83.0
  Subject: English, Average Score: 83.5

在这个示例中,我们使用了flatMap将学生的分数列表展平为一个包含班级名称、科目和成绩的条目流。然后,我们使用Collectors.groupingBy根据班级和科目进行分组,并计算每个分组的平均成绩。

4. 处理空集合和异常情况

在实际开发中,我们需要考虑到可能会遇到空集合或其他异常情况。以下是处理这些情况的示例。

案例3:处理空集合

import java.util.*;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Student> students = Collections.emptyList(); // 空集合

        Map<String, Double> averageScores = students.stream()
            .collect(Collectors.groupingBy(
                Student::getClassName,
                Collectors.averagingInt(Student::getScore)
            ));

        if (averageScores.isEmpty()) {
            System.out.println("No data available for calculation.");
        } else {
            averageScores.forEach((className, avgScore) -> {
                System.out.println("Class: " + className + ", Average Score: " + avgScore);
            });
        }
    }
}

在这个示例中,如果学生列表为空,我们会输出一条提示信息,表示没有数据可供计算。

结语

本文介绍了如何在Java中使用集合和Stream API来计算组内平均值。通过多个实际代码案例,我们展示了如何处理简单和复杂的数据结构,以及如何应对空集合和异常情况。希望这些示例能帮助你更好地理解和应用Java集合的分组平均值计算。