本文目录导读:

- 基础案例:筛选年龄大于18的用户
- 多条件筛选:名字以A开头 且 年龄大于18
- 排除/取反:筛选未成年(年龄 < 18)
- 去重后筛选:结合
distinct() - 复杂对象属性筛选
- ⚠️ 几个容易被忽略的细节(面试常问)
Java Stream 的 filter() 方法是用来筛选数据最核心的工具,它接收一个 Predicate(断言函数,即返回 boolean 的 Lambda 表达式),保留满足条件的元素。
下面为你提供 5 个从简单到复杂的实战案例,覆盖最常见的筛选场景。
基础案例:筛选年龄大于18的用户
场景:从一个用户列表中找出成年人。
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
public static void main(String[] args) {
// 准备数据
List<User> users = List.of(
new User("Alice", 25),
new User("Bob", 17),
new User("Charlie", 19),
new User("David", 16)
);
// 筛选年龄 >= 18 的用户
List<User> adults = users.stream()
.filter(user -> user.getAge() >= 18) // 关键:条件表达式
.collect(Collectors.toList());
System.out.println(adults);
// 输出:[User(name=Alice, age=25), User(name=Charlie, age=19)]
}
}
// 辅助类
class User {
private String name;
private int age;
// 构造方法、getter、toString 略(需要自己补全)
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() { return age; }
public String getName() { return name; }
@Override
public String toString() {
return "User(name=" + name + ", age=" + age + ")";
}
}
关键点:
filter()内部是 Lambda:user -> user.getAge() >= 18。- 返回
true的保留,false的丢弃。
多条件筛选:名字以A开头 且 年龄大于18
场景:筛选名字以 "A" 开头且成年的人。
List<User> result = users.stream()
.filter(user -> user.getName().startsWith("A") && user.getAge() >= 18)
.collect(Collectors.toList());
// 结果:[User(name=Alice, age=25)]
也可以分步写(更清晰):
Predicate<User> nameStartsWithA = user -> user.getName().startsWith("A");
Predicate<User> ageAdult = user -> user.getAge() >= 18;
List<User> result = users.stream()
.filter(nameStartsWithA.and(ageAdult)) // 使用 Predicate 的 and()
.collect(Collectors.toList());
排除/取反:筛选未成年(年龄 < 18)
场景:找出年龄小于 18 的用户。
直接写条件
List<User> minors = users.stream()
.filter(user -> user.getAge() < 18)
.collect(Collectors.toList());
// 结果:[User(name=Bob, age=17), User(name=David, age=16)]
使用 Predicate.negate()(取反)
Predicate<User> isAdult = user -> user.getAge() >= 18;
List<User> minors = users.stream()
.filter(isAdult.negate()) // 取反,相当于 !isAdult
.collect(Collectors.toList());
去重后筛选:结合 distinct()
场景:有一个包含重复元素的列表,筛选出偶数并去重。
List<Integer> numbers = List.of(1, 2, 2, 3, 4, 4, 5, 6);
List<Integer> evenDistinct = numbers.stream()
.filter(n -> n % 2 == 0) // 筛选偶数
.distinct() // 去重
.collect(Collectors.toList());
System.out.println(evenDistinct); // 输出:[2, 4, 6]
复杂对象属性筛选
场景:有一个订单列表 Order,每个订单包含 amount(金额)和 status(状态)。
需求:找出状态为 “PAID” 且金额大于 100 的订单。
List<Order> orders = List.of(
new Order("001", 150.0, "PAID"),
new Order("002", 50.0, "PAID"),
new Order("003", 200.0, "UNPAID")
);
List<Order> paidHighValue = orders.stream()
.filter(order -> "PAID".equals(order.getStatus()) && order.getAmount() > 100)
.collect(Collectors.toList());
System.out.println(paidHighValue);
// 输出:[Order(id=001, amount=150.0, status=PAID)]
⚠️ 几个容易被忽略的细节(面试常问)
-
filter()不会修改原数据users.stream().filter(u -> u.getAge() > 18).collect(...); // users 列表本身不变,这是函数式编程的特点(无副作用)
-
顺序问题:
filter()放在前面可以减少后续操作的数据量,提升性能。// 推荐:先过滤,再 map list.stream() .filter(...) // 先减少数据 .map(...) // 再处理少量数据 -
空指针风险:
User::getName可能为null,直接调用startsWith()会报NullPointerException。// 安全写法 .filter(user -> user.getName() != null && user.getName().startsWith("A")) // 或者使用 Objects::nonNull .filter(user -> Objects.nonNull(user.getName()) && user.getName().startsWith("A"))
| 场景 | 写法 |
|---|---|
| 单条件 | filter(x -> x > 5) |
| 多条件 AND | filter(x -> x > 5 && x < 10) |
| 多条件 OR | filter(x -> x > 5 \|\| x < 0) |
| 取反 | filter(Predicate.not(x -> x > 5)) 或 filter(x -> !(x > 5)) |
| 配合 distinct | filter(...).distinct() |
| 配合 limit | filter(...).limit(5) (只要前5个满足条件的) |
如果你有具体的业务场景(比如筛选日期范围、模糊匹配字符串等),可以告诉我,我可以帮你写出对应的案例。