
本文介绍了如何使用 Java Stream API 中的 filter 方法,结合多个函数式接口(Predicate)来实现复杂的过滤逻辑。重点讲解了如何将多个 Predicate 组合成一个,以满足不同的过滤需求,例如:所有条件都满足、至少一个条件满足等。同时,也指出了自定义函数式接口的必要性,并推荐使用 Java 内置的 Predicate 接口。
使用 Predicate 接口进行过滤
Java 8 引入了函数式接口 Predicate,它表示一个布尔值函数,接受一个参数并返回一个布尔值。Predicate 非常适合用于 Stream 的 filter 操作,可以方便地定义各种过滤条件。
例如,假设我们有一个 Student 类,并且想要过滤出所有年龄大于 18 岁的学生:
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.Getter;
class Student {
@Getter
private LocalDate birthDate;
public Student(LocalDate birthDate) {
this.birthDate = birthDate;
}
}
public class PredicateExample {
public static void main(String[] args) {
List students = List.of(
new Student(LocalDate.of(2000, 1, 1)),
new Student(LocalDate.of(2005, 1, 1)),
new Student(LocalDate.of(1995, 1, 1))
);
Predicate isAdult = s -> ChronoUnit.YEARS.between(LocalDate.now(), s.getBirthDate()) >= 18;
List adultStudents = students.stream()
.filter(isAdult)
.collect(Collectors.toList());
System.out.println("Adult Students Count: " + adultStudents.size()); // Output: 3
}
} 组合多个 Predicate
当需要应用多个过滤条件时,可以将多个 Predicate 组合成一个。Predicate 接口提供了 and、or 和 negate 方法来实现逻辑与、逻辑或和逻辑非操作。
立即学习“Java免费学习笔记(深入)”;
1. 所有条件都满足(逻辑与)
如果需要所有条件都满足,可以使用 Predicate.and() 方法。
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.Getter;
class Student {
@Getter
private LocalDate birthDate;
@Getter
private String name;
public Student(LocalDate birthDate, String name) {
this.birthDate = birthDate;
this.name = name;
}
}
public class CombinedPredicateExample {
public static void main(String[] args) {
List students = List.of(
new Student(LocalDate.of(2000, 1, 1), "Alice"),
new Student(LocalDate.of(2005, 1, 1), "Bob"),
new Student(LocalDate.of(1995, 1, 1), "Charlie"),
new Student(LocalDate.of(2000, 1, 1), "David")
);
Predicate isAdult = s -> ChronoUnit.YEARS.between(LocalDate.now(), s.getBirthDate()) >= 18;
Predicate nameStartsWithA = s -> s.getName().startsWith("A");
Predicate combinedPredicate = isAdult.and(nameStartsWithA);
List filteredStudents = students.stream()
.filter(combinedPredicate)
.collect(Collectors.toList());
System.out.println("Filtered Students Count: " + filteredStudents.size()); // Output: 1
}
} 2. 至少一个条件满足(逻辑或)
如果只需要至少一个条件满足,可以使用 Predicate.or() 方法。
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.Getter;
class Student {
@Getter
private LocalDate birthDate;
@Getter
private String name;
public Student(LocalDate birthDate, String name) {
this.birthDate = birthDate;
this.name = name;
}
}
public class CombinedPredicateExample {
public static void main(String[] args) {
List students = List.of(
new Student(LocalDate.of(2000, 1, 1), "Alice"),
new Student(LocalDate.of(2005, 1, 1), "Bob"),
new Student(LocalDate.of(1995, 1, 1), "Charlie"),
new Student(LocalDate.of(2000, 1, 1), "David")
);
Predicate isAdult = s -> ChronoUnit.YEARS.between(LocalDate.now(), s.getBirthDate()) >= 18;
Predicate nameStartsWithA = s -> s.getName().startsWith("A");
Predicate combinedPredicate = isAdult.or(nameStartsWithA);
List filteredStudents = students.stream()
.filter(combinedPredicate)
.collect(Collectors.toList());
System.out.println("Filtered Students Count: " + filteredStudents.size()); // Output: 4
}
} 3. 使用循环组合多个 Predicate
如果 Predicate 的数量很多,可以使用循环来组合它们。
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.Getter;
class Student {
@Getter
private LocalDate birthDate;
@Getter
private String name;
public Student(LocalDate birthDate, String name) {
this.birthDate = birthDate;
this.name = name;
}
}
public class CombinedPredicateExample {
public static void main(String[] args) {
List students = List.of(
new Student(LocalDate.of(2000, 1, 1), "Alice"),
new Student(LocalDate.of(2005, 1, 1), "Bob"),
new Student(LocalDate.of(1995, 1, 1), "Charlie"),
new Student(LocalDate.of(2000, 1, 1), "David")
);
List> predicates = new ArrayList<>();
predicates.add(s -> ChronoUnit.YEARS.between(LocalDate.now(), s.getBirthDate()) >= 18);
predicates.add(s -> s.getName().startsWith("A"));
predicates.add(s -> s.getName().length() > 3);
Predicate combinedPredicate = predicates.stream()
.reduce(Predicate.isEqual(true), Predicate::and);
List filteredStudents = students.stream()
.filter(combinedPredicate)
.collect(Collectors.toList());
System.out.println("Filtered Students Count: " + filteredStudents.size()); // Output: 1
}
} 避免自定义重复的函数式接口
通常情况下,没有必要自定义类似 FilterClass 的函数式接口。Java 已经提供了 Predicate 接口,它已经满足了大多数过滤需求。自定义接口会增加代码的复杂性,并且没有带来额外的优势。
总结
通过本文,我们学习了如何使用 Java Stream API 中的 filter 方法,结合多个 Predicate 来实现复杂的过滤逻辑。我们了解了如何使用 and、or 和循环来组合多个 Predicate,并避免自定义重复的函数式接口。掌握这些技巧可以帮助我们编写更加简洁、高效的 Java 代码。










