Java Stream API 中的 filter()
方法详解
在 Java 8 中引入的 Stream API 提供了一种函数式编程风格的方式来处理集合类的数据。filter()
方法是 Stream 中最常用的操作之一,它用于根据给定的条件筛选出符合条件的元素。
1. filter()
方法概述
filter()
方法用于从流中筛选出符合特定条件的元素。它接收一个 Predicate 类型的参数,该参数是一个函数接口,用于对每个元素进行条件判断。
方法签名:
Stream<T> filter(Predicate<? super T> predicate);
- T:流中元素的类型。
- predicate:一个返回布尔值的条件函数,对流中的每个元素进行判断。
返回值:
返回一个新的流,其中只包含通过 predicate
条件测试的元素。
2. filter()
方法的工作原理
filter()
会遍历流中的每个元素,检查是否满足Predicate
条件。- 只有通过
Predicate
条件测试的元素才会被保留,其他元素会被丢弃。 - 这个操作是 惰性 的,意味着
filter()
本身并不会对数据进行立即计算,直到你触发终端操作(如collect()
,forEach()
等)。
3. 示例:基本使用
假设我们有一个包含多个整数的列表,并想要筛选出其中的偶数:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 使用 filter 筛选出偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even numbers: " + evenNumbers);
}
}
输出:
Even numbers: [2, 4, 6, 8]
4. filter()
方法中的 Predicate
filter()
方法接收的 Predicate
是一个函数式接口,它表示一个接受单个输入参数并返回布尔值的函数。例如:n -> n % 2 == 0
就是一个 Predicate,它判断一个数字是否是偶数。
Predicate 接口的常用方法:
test(T t)
:检查给定参数是否符合条件,返回布尔值。and()
:与另一个 Predicate 合并,只有两个条件都为true
时,结果才为true
。or()
:与另一个 Predicate 合并,至少有一个条件为true
时,结果就为true
。negate()
:将条件取反。
5. 高级用法
5.1 多条件筛选
通过 Predicate
的 and()
或 or()
方法可以实现多条件筛选。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 使用 filter 和 and 筛选出大于 3 且为偶数的数字
List<Integer> filteredNumbers = numbers.stream()
.filter(n -> n > 3)
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Filtered numbers: " + filteredNumbers);
}
}
输出:
Filtered numbers: [4, 6, 8]
5.2 结合其他操作
filter()
方法可以与其他 Stream 操作结合使用,形成更复杂的数据处理管道。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 筛选出以 'b' 开头的单词并转换为大写
List<String> filteredWords = words.stream()
.filter(word -> word.startsWith("b"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Filtered words: " + filteredWords);
}
}
输出:
Filtered words: [BANANA]
5.3 空值过滤
filter()
方法还可以用于移除流中的空值。例如,过滤掉字符串流中的 null
元素:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", null, "cherry", "date", null);
// 使用 filter 过滤掉 null 值
List<String> nonNullWords = words.stream()
.filter(word -> word != null)
.collect(Collectors.toList());
System.out.println("Non-null words: " + nonNullWords);
}
}
输出:
Non-null words: [apple, banana, cherry, date]
5.4 惰性计算
filter()
是惰性计算的,这意味着在调用 filter()
时,它不会立即执行操作,只有在调用终端操作(如 collect()
、forEach()
等)时,才会触发计算。
import java.util.Arrays;
import java.util.List;
public class StreamFilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 使用 filter 过滤出偶数,并通过 forEach 打印每个元素
numbers.stream()
.filter(n -> {
System.out.println("Filtering " + n);
return n % 2 == 0;
})
.forEach(System.out::println);
}
}
输出:
Filtering 1
Filtering 2
2
Filtering 3
Filtering 4
4
Filtering 5
Filtering 6
6
Filtering 7
Filtering 8
8
Filtering 9
可以看到,filter()
会遍历所有的元素并判断条件,但只有在调用 forEach()
时,元素才会被真正打印。
6. 常见问题
- 性能:
filter()
是惰性计算的,它只会在需要时才执行过滤操作,不会对所有元素都进行过滤,从而减少了不必要的计算。 - 短路操作:与其他操作(如
findFirst()
、anyMatch()
等)配合使用时,filter()
可能会发挥短路特性,只对必要的元素进行处理。 - 类型安全:
filter()
会保证类型安全,确保只能使用与流元素类型兼容的条件进行过滤。
7. 总结
filter()
方法是 Stream API 中的重要方法,用于基于条件筛选元素。- 它接收一个
Predicate
类型的函数参数,用于判断每个元素是否符合条件。 filter()
方法返回一个新的 Stream,原始 Stream 不会改变。- 它是惰性操作,只有在触发终端操作时才会执行过滤过程。
- 可以与其他 Stream 操作(如
map()
、collect()
等)结合使用,形成功能强大的数据处理管道。
希望这些信息能帮助你更好地理解 Java Stream API 中的 filter()
方法。如果有其他问题或需要更详细的示例,随时告诉我!
发表回复