使用 Java 8 的 BiPredicate 和 Stream API 进行数据过滤和分组

发布于:2024-08-02 ⋅ 阅读:(62) ⋅ 点赞:(0)

在本文中,我们将学习如何使用 Java 8 的 BiPredicate 和 Stream API 来进行数据过滤和分组。我们将通过一个具体的例子来演示这一过程,例子中包含学生成绩的筛选和基于考试时间段的分组。

案例介绍

我们有两个实体类:StudentScoreExamTimePeriodStudentScore 代表学生的考试成绩,包含学生姓名、考试时间和分数;ExamTimePeriod 代表考试的时间段,包含开始时间、结束时间和考试轮次。

Maven依赖

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.27</version>
        </dependency>

实体类定义

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 考试的时间段,包含开始时间、结束时间和考试轮次
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ExamTimePeriod {
    private String startTime;
    private String endTime;
    private String rounds;
}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
 * 学生的考试成绩,包含学生姓名、考试时间和分数
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public  class StudentScore {
    private String studentName;
    private LocalDateTime scoreTime;
    private double score;

}

测试数据生成



import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

public class BiPredicateDemo {
    public static void main(String[] args) {
        // 生成测试数据
        List<StudentScore> studentScores = Arrays.asList(
                new StudentScore("Alice", LocalDateTime.parse("2023-07-29T08:00:00"), 85.0),
                new StudentScore("Bob", LocalDateTime.parse("2023-07-29T18:00:00"), 90.0),
                new StudentScore("Charlie", LocalDateTime.parse("2023-07-29T12:00:00"), 95.0),
                new StudentScore("John", LocalDateTime.parse("2023-07-29T07:00:00"), 88.0),
                new StudentScore("David", LocalDateTime.parse("2023-07-29T20:00:00"), 80.0)
        );

        ExamTimePeriod morningTimePeriod = new ExamTimePeriod("06:00:00", "12:00:00", "晨考");
        ExamTimePeriod afternoonTimePeriod = new ExamTimePeriod("12:00:00", "18:00:00", "午考");
        ExamTimePeriod eveningTimePeriod = new ExamTimePeriod("18:00:00", "22:00:00", "晚考");
        List<ExamTimePeriod> examTimePeriodList = new ArrayList<>();
        examTimePeriodList.add(morningTimePeriod);
        examTimePeriodList.add(afternoonTimePeriod);
        examTimePeriodList.add(eveningTimePeriod);
        // 创建一个 BiPredicate 来过滤学生成绩,筛选出成绩高于85分的学生。
        filterScores(studentScores);
        System.out.println("-----------");
        // 按照考试时间段(早上、中午、晚上)进行分组。
        filterScoresByTimePeriod(studentScores, morningTimePeriod, afternoonTimePeriod, eveningTimePeriod);
        System.out.println("=============");
        // 过滤出成绩高于 minScore 且考试时间在指定时间段(examTimePeriod)内的学生成绩。
        List<StudentScore> studentScoresList1 = filterScoresWithMinScore(studentScores, 85.0, morningTimePeriod);
        System.out.println(studentScoresList1);
        // 过滤出所有成绩在指定范围(scoreRange)内且考试时间在指定时间段(examTimePeriod)内的学生列表。
        List<StudentScore> studentScoreList2 = filterScoresWithScoreRangeAndTimePeriod(studentScores, new Double[]{80.0, 95.0}, morningTimePeriod);
        System.out.println(studentScoreList2);
        System.out.println("*************");
        // 过滤出成绩在指定范围内的学生(例如,成绩在80到95之间),并根据考试时间段(早上、下午、晚上)进行分组。
        Map<String, List<StudentScore>> groupedScores = groupAndFilterScores(studentScores, examTimePeriodList);
        System.out.println(groupedScores);
    }
}

功能实现

创建一个 BiPredicate 来过滤学生成绩,筛选出成绩高于85分的学生。
    /**
     * 创建一个 BiPredicate 来过滤学生成绩,筛选出成绩高于85分的学生。
     *
     * @param studentScores 学生成绩列表
     */
    private static void filterScores(List<StudentScore> studentScores) {
        BiPredicate<StudentScore, Double> scoreFilter = (item, score) -> item.getScore() > score;
        List<StudentScore> collect = studentScores.stream().filter(item -> scoreFilter.test(item, 85.0))
                .collect(Collectors.toList());
        System.out.println(collect);
    }
使用 BiPredicate 过滤学生成绩,按照考试时间段(早上、中午、晚上)进行筛选。
 /**
     * 使用 BiPredicate 过滤学生成绩,按照考试时间段(早上、中午、晚上)进行筛选。
     *
     * @param studentScores 学生成绩列表
     * @param morningTimePeriod 早上考试时间段
     * @param afternoonTimePeriod 中午考试时间段
     * @param eveningTimePeriod 晚上考试时间段
     */
    private static void filterScoresByTimePeriod(List<StudentScore> studentScores, ExamTimePeriod morningTimePeriod,
                          ExamTimePeriod afternoonTimePeriod, ExamTimePeriod eveningTimePeriod) {
        // 使用 BiPredicate 进行数据过滤
        BiPredicate<StudentScore, ExamTimePeriod> timePeriodFilter = (item, config) -> {
            String scoreTime = item.getScoreTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN));
            return scoreTime.compareTo(config.getStartTime()) >= 0 && scoreTime.compareTo(config.getEndTime()) < 0;
        };

        List<StudentScore> morningScores = filterScores(studentScores, morningTimePeriod, timePeriodFilter);
        List<StudentScore> afternoonScores = filterScores(studentScores, afternoonTimePeriod, timePeriodFilter);
        List<StudentScore> eveningScores = filterScores(studentScores, eveningTimePeriod, timePeriodFilter);
        System.out.println("早上考试:" + morningScores);
        System.out.println("中午考试:" + afternoonScores);
        System.out.println("晚上考试:" + eveningScores);
    }
    /**
     * 过滤学生成绩,使用指定的时间段过滤器(BiPredicate)。
     *
     * @param studentScores 学生成绩列表
     * @param timePeriodConfig 时间段配置
     * @param filter 过滤器
     * @return 符合条件的学生成绩列表
     */
    private static List<StudentScore> filterScores(List<StudentScore> studentScores, ExamTimePeriod timePeriodConfig, BiPredicate<StudentScore, ExamTimePeriod> filter) {
        return studentScores.stream()
                .filter(item -> filter.test(item, timePeriodConfig))
                .collect(Collectors.toList());
    }
过滤成绩高于 minScore 且考试时间在指定时间段(examTimePeriod)内的学生成绩。
/**
     * 过滤成绩高于 minScore 且考试时间在指定时间段(examTimePeriod)内的学生成绩。
     *
     * @param studentScores 学生成绩列表
     * @param minScore 最低成绩
     * @param examTimePeriod 考试时间段
     * @return 符合条件的学生成绩列表
     */
    private static List<StudentScore> filterScoresWithMinScore(List<StudentScore> studentScores, double minScore,
                                        ExamTimePeriod examTimePeriod) {
        BiPredicate<StudentScore, Double> scoreFilter = (student, score) -> student.getScore() > score;
        BiPredicate<StudentScore, ExamTimePeriod> timePeriodFilter = (student, config) -> {
            String scoreTime = LocalDateTimeUtil.format(student.getScoreTime(), DatePattern.NORM_TIME_PATTERN);
            return scoreTime.compareTo(config.getStartTime()) >= 0 && scoreTime.compareTo(config.getEndTime()) < 0;
        };

        return studentScores.stream()
                .filter(student -> scoreFilter.test(student, minScore) && timePeriodFilter.test(student, examTimePeriod))
                .collect(Collectors.toList());
    }
过滤出所有成绩在指定范围(scoreRange)内且考试时间在指定时间段(examTimePeriod)内的学生列表。
/**
     * 过滤出所有成绩在指定范围(scoreRange)内且考试时间在指定时间段(examTimePeriod)内的学生列表。
     *
     * @param studentScores 学生成绩列表
     * @param scoreRange 成绩范围
     * @param examTimePeriod 考试时间段
     * @return 符合条件的学生成绩列表
     */
    private static List<StudentScore> filterScoresWithScoreRangeAndTimePeriod(List<StudentScore> studentScores, Double[] scoreRange,
                                                    ExamTimePeriod examTimePeriod) {
        BiPredicate<StudentScore, Double[]> scoreRangeFilter = (student, range) -> student.getScore() >= range[0] && student.getScore() <= range[1];
        BiPredicate<StudentScore, ExamTimePeriod> timePeriodFilter = (student, timePeriod) -> {
            String scoreTime = student.getScoreTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN));
            return scoreTime.compareTo(timePeriod.getStartTime()) >= 0 && scoreTime.compareTo(timePeriod.getEndTime()) < 0;
        };

        return studentScores.stream()
                .filter(student -> scoreRangeFilter.test(student, scoreRange) && timePeriodFilter.test(student, examTimePeriod))
                .collect(Collectors.toList());
    }
过滤出成绩在指定范围内的学生(例如,成绩在80到95之间),并根据考试时间段(早上、下午、晚上)进行分组。
/**
     * 过滤出成绩在指定范围内的学生(例如,成绩在80到95之间),
     * 并根据考试时间段(早上、下午、晚上)进行分组。
     *
     * @param studentScoreList 学生成绩列表
     * @param examTimePeriodList 考试时间段列表
     * @return 分组后的学生成绩映射
     */
    private static Map<String, List<StudentScore>> groupAndFilterScores(List<StudentScore> studentScoreList, List<ExamTimePeriod> examTimePeriodList) {
        // 成绩区间
        Double[] scoreRange = {80.0, 95.0};

        // 定义成绩范围过滤器
        BiPredicate<StudentScore, Double[]> scoreRangeFilter = (student, range) ->
                student.getScore() >= range[0] && student.getScore() <= range[1];

        // 定义考试时间段过滤器
        BiPredicate<StudentScore, ExamTimePeriod> timePeriodFilter = (student, period) -> {
            String scoreTime = student.getScoreTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN));
            return scoreTime.compareTo(period.getStartTime()) >= 0 && scoreTime.compareTo(period.getEndTime()) < 0;
        };

        // 使用 HashMap 分组
        Map<String, List<StudentScore>> groupedScores = new HashMap<>();
        for (ExamTimePeriod timePeriod : examTimePeriodList) {
            List<StudentScore> filteredScores = studentScoreList.stream()
                    .filter(student -> scoreRangeFilter.test(student, scoreRange)
                            && timePeriodFilter.test(student, timePeriod))
                    .collect(Collectors.toList());

            groupedScores.put(timePeriod.getRounds(), filteredScores);
        }
/*        // 使用 groupingBy 进行分组
        Map<String, List<StudentScore>> groupedScores =
                studentScoreList.stream().filter(student -> scoreRangeFilter.test(student, scoreRange))
                        .collect(Collectors.groupingBy(student -> {
                            for (ExamTimePeriod timePeriod : examTimePeriodList) {
                                String scoreTime = student.getScoreTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN));
                                if (scoreTime.compareTo(timePeriod.getStartTime()) >= 0 && scoreTime.compareTo(timePeriod.getEndTime()) < 0) {
                                    return timePeriod.getRounds();
                                }
                            }
                            return "非正常考试时间段";
                        }));*/
        return groupedScores;
    }

总结

通过本例,我们学习了如何使用 Java 8 的 BiPredicate 和 Stream API 进行复杂的过滤和分组操作。使用 BiPredicate 可以使代码更加清晰和灵活,非常适合用于需要多个条件进行筛选的场景。