介绍
Java Stream API 是 Java 8 中新引入的一种数据处理方式。它的目标是提供一种简单、高效、并行的处理集合、数组等数据源的方式,以取代原有的 for 循环和迭代器。Java Stream API 通过使用聚合操作(aggregation operation)操纵数据流,获取或转换数据。
前置知识
在深入学习 Java Stream API 之前,我们需要先掌握以下概念:
- 函数式接口(Functional Interface):函数式接口是只有一个抽象方法的接口。
- Lambda 表达式:Lambda 表达式 是一种简洁的,代码行数少的而且可传递的对象,可以将 Lambda 表达式看作一段可以传递的代码。它无需命名,旨在简化 Java 程序中单一方法的某些语法的编写。Lambda 方式可以使用 Functional Interface 来实现。
Stream API 的主要特点
Java Stream API 的主要特点可归结为以下几点:
- 内部迭代:Java Stream API 在迭代时会自动选择并行或顺序执行,无需手动进行线程管理。
- 一次执行:可以对源数据执行各种中间过程和操作,最终只进行一次完成操作。
- 不可变:Java Stream API 操作的结果是新流,源数据不变。
- 延迟执行:Java Stream API 的操作是惰性的,只有在最终执行中间过程的操作时才会被执行。
常用操作
筛选操作
筛选操作指的是通过保留或去除元素来筛选出需要的结果。以下是常用的筛选操作。
filter(Predicate)
filter
方法接收一个 Predicate 函数式接口作为参数,返回一个只包含符合条件的元素的 Stream。
Stream<T> filter(Predicate<? super T> predicate)
示例:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
Stream<String> filtered = strings.stream().filter(string -> !string.isEmpty());
filtered.forEach(System.out::println); //输出不为空的字符串,即除了“”之外的字符串
distinct()
distinct
方法返回由不同的元素组成的 Stream。
Stream<T> distinct()
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 1, 3, 2, 4);
Stream<Integer> distinctNumbers = numbers.stream().distinct();
distinctNumbers.forEach(System.out::println); //输出不重复的整数
映射操作
映射操作指的是将一个流中的元素映射到另一个流中。以下是常用的映射操作。
map(Function)
map
方法用于将元素按照指定的 Function 转换成另外的元素。Function 函数式接口接收一个参数并返回一个值。
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
示例:
List<String> strings = Arrays.asList("abc", "efg", "qwerty");
Stream<Integer> mapped = strings.stream().map(String::length); //映射字符串的长度
mapped.forEach(System.out::println); //输出各个字符串的长度
flatMap(Function)
flatMap
方法接收一个 Function 函数式接口作为参数,可以将嵌套的 Stream 展平成一个 Stream。
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
示例:
List<List<String>> list = new ArrayList<>();
list.add(Arrays.asList("apple", "banana"));
list.add(Arrays.asList("orange", "lemon", "grape"));
Stream<String> flatMapped = list.stream().flatMap(Collection::stream);
flatMapped.forEach(System.out::println); //输出展平后的字符串
排序操作
排序操作指对数据集合进行排序。Java Stream API 提供了两种方式进行排序:自然排序和定制排序。
sorted()
sorted
方法根据自然排序对源集合进行排序。
Stream<T> sorted()
示例:
List<String> strings = Arrays.asList("ccc", "aaa", "bbb", "ddd");
Stream<String> sorted = strings.stream().sorted();
sorted.forEach(System.out::println); //自然排序
sorted(Comparator)
sorted
方法根据指定的 Comparator 接口对源集合进行排序。
Stream<T> sorted(Comparator<? super T> comparator)
示例:
List<String> strings = Arrays.asList("ccc", "aaa", "bbb", "ddd");
Stream<String> sorted = strings.stream().sorted((s1,s2)->s1.length()-s2.length());
sorted.forEach(System.out::println); //按字符串长度排序
聚合操作
聚合操作指获取操作结果的操作,包括归约、收集等操作。
reduce(T identity, BinaryOperator)
reduce
方法接收一个初始值和一个 BinaryOperator(t,t)->t 函数,将集合中的元素逐一与上一个结果进行操作,返回一个包含操作结果的 Optional 对象。
T reduce(T identity, BinaryOperator<T> accumulator)
示例:
List<Integer> numbers = Arrays.asList(1,2,3,4);
Optional<Integer> sum = numbers.stream().reduce(0, (x,y)->x+y); //计算整数的和
sum.ifPresent(System.out::println); //输出求和的结果
collect(Collector)
collect
方法使用 Collectors 工厂类提供的方法将流转换成集合、映射、字符串等,以操作结果的形式返回。
R collect(Collector<? super T, A, R> collector)
示例:
List<String> strings = Arrays.asList("abc", "def", "ghi");
List<String> collected = strings.stream().filter(s->s.startsWith("a")).collect(Collectors.toList());
collected.forEach(System.out::println); //输出以“a”开头的字符串
小结
Java Stream API 通过内部迭代的方式提供了一种高效、并行的数据处理方式,避免了原有的 for 循环和迭代器的繁琐操作。通过上述常用操作,我们可以方便地对集合、数据进行筛选、映射、排序、聚合等操作,以得到我们预期的结果。通过学习 Java Stream API,我们可以更加轻松地对数据进行操作,提高开发效率。