在本文中,我们将学习如何使用 Java 8 的 BiPredicate
和 Stream API 来进行数据过滤和分组。我们将通过一个具体的例子来演示这一过程,例子中包含学生成绩的筛选和基于考试时间段的分组。
案例介绍
我们有两个实体类:StudentScore
和 ExamTimePeriod
。StudentScore
代表学生的考试成绩,包含学生姓名、考试时间和分数;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
可以使代码更加清晰和灵活,非常适合用于需要多个条件进行筛选的场景。