Java 8引入了流(Stream)这一新的功能,它使得处理集合对象更加高效且易于编写。流式编程允许在数据上执行诸如过滤、映射、归约等操作,而无需显式地循环迭代数据。
1. 流的概念
在Java中,流是一种抽象,用于表示来自数据源的数据项序列。流的操作可以分为中间操作和终端操作。中间操作会返回一个新的流,允许进行链接调用;而终端操作则会执行某种计算,并返回结果。
2. 创建流
创建流有多种方式:
- 从集合创建流:
List<String> list = ...; Stream<String> stream = list.stream();
- 从数组创建流:
String[] array = ...; Stream<String> stream = Arrays.stream(array);
- 使用
Stream.of()
或Stream.iterate()
或Stream.generate()
来创建流。
3. 中间操作
中间操作不会执行任何计算,直到遇到一个终端操作才会触发。常见的中间操作包括:
filter(Predicate<T> predicate)
:过滤流中的元素。map(Function<T, R> mapper)
:将流中的元素映射为另一种形式或提取信息。flatMap(Function<T, Stream<R>> mapper)
:将流中的每个值都转换成一个流,然后把所有这些流合并成一个流。distinct()
:去除重复元素。sorted()
:排序流中的元素。peek(Consumer<T> action)
:用于调试,输出流中的每一个元素。
4. 终端操作
终端操作会从流产生结果或副作用。一旦执行,流就被认为是“用尽”了,不能再次使用。常见的终端操作包括:
forEach(Consumer<T> action)
:遍历流中的元素。collect(Collector<T,A,R> collector)
:收集流中的元素,将其转换为其他形式,如列表、集合等。reduce(BinaryOperator<T>)
:将流中的元素反复结合起来,得到一个单一的结果。anyMatch/Predicate<T> predicate)/allMatch/noneMatch
:检查是否至少有一个/全部/没有元素匹配给定的条件。findFirst/FindAny
:返回第一个/任意一个匹配的元素。count
:返回流中的元素数量。
5. 常用方法与技巧
- 延迟求值:中间操作是惰性的,只有在执行终端操作时才会触发计算。
- 链式调用:多个中间操作可以链接起来形成一个管道,依次对流中的数据进行处理。
- 并行流:通过
parallelStream()
方法可以创建并行流,利用多核处理器提高处理速度,但需要注意并行操作可能导致线程安全问题。
6. 实例
有一个用户列表,需要找到年龄大于18岁的用户的名字,并按字母顺序排序,最后将它们收集到一个列表中。
List<User> users = ...;
List<String> names = users.stream()
.filter(user -> user.getAge() > 18)
.map(User::getName)
.sorted()
.collect(Collectors.toList());