Java Stream 的主要作用有以下几个方面:
为什么使用流式编程可以提高代码可读性和简洁性
声明式编程风格:流式编程采用了一种声明式的编程风格,你只需描述你想要对数据执行的操作,而不需要显式地编写迭代和控制流语句。
链式调用:流式编程使用方法链式调用的方式,将多个操作链接在一起。每个方法都返回一个新的流对象,这样你可以像“流水线”一样在代码中顺序地写下各种操作,使代码逻辑清晰明了。
操作的组合:流式编程提供了一系列的操作方法,如过滤、映射、排序、聚合等,这些方法可以按照需要进行组合使用。你可以根据具体的业务需求将这些操作串联起来,形成一个复杂的处理流程,而不需要编写大量的循环和条件语句。
减少中间状态:传统的迭代方式通常需要引入中间变量来保存中间结果,这样会增加代码的复杂度和维护成本。而流式编程将多个操作链接在一起,通过流对象本身来传递数据,避免了中间状态的引入。
减少循环和条件:流式编程可以替代传统的循环和条件语句的使用。
Stream(流)是一个来自数据源的元素队列并支持聚合操作
Stream中的操作大体可以分为两类
过滤操作(filter),它接受一个 Predicate 函数作为参数,用于过滤 Stream 中的元素。只有满足 Predicate 条件的元素会被保留下来,而不满足条件的元素将被过滤掉。
过滤操作的语法如下
Stream<T> filter(Predicate<? super T> predicate)
其中,T 表示 Stream 元素的类型,predicate 是一个函数式接口 Predicate 的实例,它的泛型参数和 Stream 元素类型一致,并且predicate返回的值必须是boolean类型,因为需要通过真假值判断是否要过滤该值。
使用过滤操作可以根据自定义的条件来筛选出符合要求的元素,从而对 Stream 进行精确的数据过滤。
下面是一个示例,演示如何使用过滤操作筛选出一个整数流中的大于三的数:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 生成流式对象
// Stream stream = numbers.stream();
List<Integer> list=numbers.stream().filter(
(x)->{return x>3;} //lambda表达式 可简化成下面的写法
// x->x>3
).toList();
System.out.println("number中大于3的数: "+list.toString());
映射操作(map),它接受一个 Function 函数作为参数,用于对 Stream 中的每个元素进行映射转换,生成一个新的 Stream。
映射操作的语法如下:
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
其中,T 表示原始 Stream 的元素类型,R 表示映射后的 Stream 的元素类型,mapper 是一个函数式接口 Function 的实例,可以进行不同的映射操作.
下面是一个示例,演示如何使用映射操作将一个字符串流中的每个字符串转换为其长度:
List<String> numbers = Arrays.asList("apple", "banana", "cherry");
// 生成流式对象
// Stream stream = numbers.stream();
numbers.stream().map(
String::length
).forEach(System.out::println);
在这个示例中,我们首先创建了一个包含字符串的 Stream,并调用 map() 方法传入String::length,表示要将每个字符串转换为其长度。然后通过 forEach() 方法遍历输出结果。
注意: 映射操作可能引发空指针异常(NullPointerException),因此在执行映射操作时,应确保原始 Stream 中不包含空值,并根据具体情况进行空值处理。
排序操作(sorted)是 Stream API 中的一种常用操作方法,它用于对 Stream 中的元素进行排序。排序操作可以按照自然顺序或者使用自定义的比较器进行排序。
排序操作的语法如下:
Stream<T> sorted() Stream<T> sorted(Comparator<? super T> comparator)
第一种语法形式中,sorted() 方法会根据元素的自然顺序进行排序。如果元素实现了 Comparable 接口并且具备自然顺序,那么可以直接调用该方法进行排序。
第二种语法形式中,sorted(Comparator super T> comparator) 方法接受一个比较器(Comparator)作为参数,用于指定元素的排序规则。通过自定义比较器,可以对非 Comparable 类型的对象进行排序。
下面是一个示例,演示如何使用排序操作对一个字符串流进行排序:
List<String> numbers = Arrays.asList("apple", "banana", "cherry");
// 生成流式对象
// Stream stream = numbers.stream();
numbers.stream().sorted().forEach(System.out::println);
输出
apple
banana
cherry
注意: 排序操作可能会影响程序的性能,特别是对于大型数据流或者复杂的排序规则。因此,在实际应用中,需要根据具体情况进行权衡和优化,选择合适的算法和数据结构来提高排序的效率。
截断操作(limit和skip),用于在处理流的过程中对元素进行截断。
注意: 在使用截断操作时需要注意流的有界性。如果流是无界的(例如 Stream.generate()),那么使用 limit() 方法可能导致程序陷入无限循环,而使用 skip() 方法则没有意义。
forEach和peek都是Stream API中用于遍历流中元素的操作方法,它们在处理流的过程中提供了不同的功能和使用场景。
forEach: forEach是一个终端操作方法,它接受一个Consumer函数作为参数,对流中的每个元素执行该函数。forEach会遍历整个流,对每个元素执行相同的操作。
示例代码:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.forEach(System.out::println);
peek: peek是一个中间操作方法,它接受一个Consumer函数作为参数,对流中的每个元素执行该函数。与forEach不同的是,peek方法会返回一个新的流,该流中的元素和原始流中的元素相同。
示例代码:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.peek(System.out::println)
.toList();
reduce和collect都是Stream API中用于聚合操作的方法,它们可以将流中的元素进行汇总、计算和收集。
reduce: reduce是一个终端操作方法,它接受一个BinaryOperator函数作为参数,对流中的元素逐个进行合并操作,最终得到一个结果。该方法会将流中的第一个元素作为初始值,然后将初始值与下一个元素传递给BinaryOperator函数进行计算,得到的结果再与下一个元素进行计算,以此类推,直到遍历完所有元素。
示例代码:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().reduce(Integer::sum).ifPresent(System.out::println);//输出结果15
在这个示例中,我们创建了一个包含整数的List,并通过stream()方法将其转换为流。然后使用reduce方法对流中的元素进行求和操作,将每个元素依次相加,得到结果15。
在 Stream API 中,allMatch、anyMatch 和 noneMatch 是用于进行匹配操作的方法,它们可以用来检查流中的元素是否满足特定的条件。
allMatch: allMatch 方法用于判断流中的所有元素是否都满足给定的条件。当流中的所有元素都满足条件时,返回 true;如果存在一个元素不满足条件,则返回 false。
anyMatch: anyMatch 方法用于判断流中是否存在至少一个元素满足给定的条件。当流中至少有一个元素满足条件时,返回 true;如果没有元素满足条件,则返回 false。
在这个示例中,我们创建了一个包含整数的 List,并通过 stream() 方法将其转换为流。然后使用 anyMatch 方法判断流中是否存在偶数。由于列表中存在偶数,所以返回 true。
noneMatch: noneMatch 方法用于判断流中的所有元素是否都不满足给定的条件。当流中没有元素满足条件时,返回 true;如果存在一个元素满足条件,则返回 false。
示例代码:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0);
System.out.println(allEven); // 输出结果: false
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
System.out.println(hasEven); // 输出结果: true
boolean noneNegative = numbers.stream()
.noneMatch(n -> n < 0);
System.out.println(noneNegative); // 输出结果: true
在 Stream API 中,findFirst 和 findAny 是用于查找操作的方法,它们可以用来从流中获取满足特定条件的元素。
findFirst: findFirst 方法用于返回流中的第一个元素。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的第一个元素的 Optional。
findAny: findAny 方法用于返回流中的任意一个元素。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的任意一个元素的 Optional。在顺序流中,通常会返回第一个元素;而在并行流中,由于多线程的处理,可能返回不同的元素。
示例代码:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> first = names.stream()
.findFirst();
first.ifPresent(System.out::println); // 输出结果: Alice
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> any = numbers.stream()
.filter(n -> n % 2 == 0)
.findAny();
any.ifPresent(System.out::println); // 输出结果: 2 或 4(取决于并行处理的结果)
在 Stream API 中,count、max 和 min 是用于统计操作的方法,它们可以用来获取流中元素的数量、最大值和最小值。
count: count 方法用于返回流中元素的数量。它返回一个 long 类型的值,表示流中的元素个数。
max: max 方法用于返回流中的最大值。
min: min 方法用于返回流中的最小值。它返回一个 Optional 对象,如果流为空,则返回一个空的 Optional;如果流非空,则返回流中的最小值的 Optional。
示例代码:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream()
.count();
System.out.println(count); // 输出结果: 5
Optional<Integer> max = numbers.stream()
.max(Integer::compareTo);
max.ifPresent(System.out::println); // 输出结果: 5
Optional<Integer> min = numbers.stream()
.min(Integer::compareTo);
min.ifPresent(System.out::println); // 输出结果: 1