Stream 流式编程
# Stream 流式编程
# 1. 认识流式编程
# 1.1 流式编程的概念和作用
Java 流(Stream
)是用于处理数据的一个功能强大的工具,它是基于函数式编程思想的一种实现。流可以看作是数据元素的序列,能够以声明式的方式对数据进行处理。
主要作用:
- 简化集合操作: 传统的集合操作往往需要借助循环、迭代器等显式地处理数据,而流式编程能以更直观的方式处理集合数据,代码更加简洁。
- 延迟计算: 流的操作是惰性的,只有在最终需要结果时才会执行。这意味着可以在定义数据处理流程时根据实际需求动态调整,优化性能。
- 并行处理: Java 8 的流支持并行处理,能够轻松利用多核处理器,提升执行效率。
- 函数式编程: 流式编程高度依赖函数式编程,使用 lambda 表达式能使代码更简洁、更具可维护性。
# 1.2 流式编程如何提高代码可读性和简洁性
- 声明式编程风格: 只需描述对数据的操作,避免了繁琐的迭代和控制流,代码更加直观。
- 链式调用: 流式操作支持方法链式调用,可以将多个操作串联在一起,形成“流水线”式的处理流程。
- 操作的组合: 流提供丰富的操作方法(如过滤、映射、聚合等),这些操作可以灵活组合使用,形成复杂的数据处理流程。
- 减少中间状态: 不需要像传统方式那样引入中间变量或临时集合来保存中间结果,代码更加简洁。
- 减少循环和条件判断: 流提供了便捷的过滤、映射和聚合操作,避免繁琐的循环和条件判断。
# 2. 流的基础示例
# 2.1 环境搭建
我们首先创建一个演员类:
package com.trs.stream;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : scholar
* @version 1.0
* @date 2023-10-24 9:42
* @description : 示例
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Actor {
/* 演员类包含演员ID、姓名、年龄和作品 */
private Integer id;
private String name;
private Integer age;
private String works;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
基于这个类,我们初始化一个演员集合:
package com.trs.stream;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
/**
* @author : scholar
* @version 1.0
* @date 2023-10-24 9:42
* @description : 示例
*/
public class StreamTest01 {
// 初始化演员列表
public static final List<Actor> actorList = Arrays.asList(
new Actor(1001, "张三", 30, "演员"),
new Actor(1002, "李四", 70, "演员"),
new Actor(1003, "王五", 40, "演员"),
new Actor(1004, "赵六", 53, "演员"),
new Actor(1005, "莉莉", 35, "演员"),
new Actor(1006, "杨晓雪", 12, "演员"),
new Actor(1007, "李师傅", 55, "演员"),
new Actor(1008, "王胖子", 40, "演员"),
new Actor(1009, "齐肩发", 45, "演员")
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 2.2 使用 Java 8 之前的做法
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @description: 使用传统方式筛选、排序并输出演员信息
*/
public class StreamTest01 {
@Test
public void test1() {
List<Actor> ageList = new ArrayList<>();
// 筛选出年龄小于40岁的演员
for (Actor actor : actorList) {
if (actor.getAge() < 40) {
ageList.add(actor);
}
}
// 按演员ID升序排序
Collections.sort(ageList, new Comparator<Actor>() {
@Override
public int compare(Actor a1, Actor a2) {
return Integer.compare(a1.getId(), a2.getId());
}
});
// 将演员姓名存入列表
List<String> actorNames = new ArrayList<>();
for (Actor actor : ageList) {
actorNames.add(actor.getName());
}
// 输出结果
for (Actor actor : ageList) {
System.out.println(actor);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
输出结果:
Actor{id=1001, name='张三', age=30, works='演员'}
Actor{id=1005, name='莉莉', age=35, works='演员'}
Actor{id=1006, name='杨晓雪', age=12, works='演员'}
2
3
这种传统的方式实现功能较为繁琐,需要手动管理集合、迭代和排序等逻辑,代码复杂且容易出错。下一步,我们可以通过流式编程大幅简化这段代码。
# 2.3 使用 Stream 流式编程
使用流式编程的方式,我们可以通过链式调用的方式完成数据的过滤、排序和处理,代码更加简洁且直观。下面是使用流式编程实现上述功能的代码:
import org.junit.jupiter.api.Test;
import java.util.List;
import static java.util.stream.Collectors.toList;
import static java.util.Comparator.comparing;
/**
* @description: 使用流式编程筛选、排序并输出演员信息
*/
public class StreamTest01 {
@Test
public void test2() {
actorList.stream()
// 过滤演员年龄小于40岁的
.filter(actor -> actor.getAge() < 40)
// 按演员ID进行升序排序
.sorted(comparing(Actor::getId))
// 提取演员的姓名
.map(Actor::getName)
// 将结果收集为 List
.collect(toList())
// 输出每个演员的姓名
.forEach(System.out::println);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
代码解析:
filter
: 筛选出年龄小于 40 岁的演员。sorted
: 使用Actor
的 ID 进行升序排序。map
: 将每个演员对象转换为其姓名。collect
: 将结果收集为一个List
。forEach
: 逐一输出每个演员的姓名。
输出结果:
张三
莉莉
杨晓雪
2
3
# 2.4 传统方式与流式编程的对比
特性 | 传统方式 | 流式编程 |
---|---|---|
代码可读性 | 代码较为繁琐,逻辑分散 | 代码简洁,操作直观 |
中间状态管理 | 需要手动管理中间集合和变量 | 通过流的操作链自动处理中间状态 |
函数式编程支持 | 基于循环、条件判断 | 支持 lambda 表达式和函数式编程 |
并行处理能力 | 手动实现并行处理 | 可以直接使用 parallelStream 进行并行处理 |
灵活性和组合性 | 扩展性差,组合操作复杂 | 提供了丰富的组合操作,如 map 、filter 等 |
# 3. 创建 Stream
创建 Stream 是一切操作的起点。Stream 可以从集合、数组、值序列等多种方式创建。下面介绍几种常见的创建方法。
# 3.1 通过集合创建 Stream
方法介绍:
- 方法:
stream()
和parallelStream()
- 语法:
default Stream<E> stream()
或default Stream<E> parallelStream()
- 参数: 无
- 返回值: 返回一个顺序流或并行流。
- 作用: 从集合中创建 Stream,用于顺序或并行处理集合元素。
代码示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamCreationExample {
public static void main(String[] args) {
// 创建一个集合
List<String> list = Arrays.asList("apple", "banana", "orange");
// 创建顺序流
Stream<String> stream = list.stream();
// 创建并行流
Stream<String> parallelStream = list.parallelStream();
stream.forEach(System.out::println); // 顺序输出集合中的元素
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3.2 通过数组创建 Stream
方法介绍:
- 方法:
Arrays.stream()
- 语法:
static <T> Stream<T> stream(T[] array)
或public static IntStream stream(int[] array)
- 参数: 需要传入一个数组,可以是对象数组或基本类型数组。
- 返回值: 返回一个流,流的类型与数组元素类型一致。
- 作用: 将数组转换为 Stream,以便对数组元素进行流式处理。
代码示例:
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamCreationFromArrayExample {
public static void main(String[] args) {
// 创建一个整数数组
int[] numbers = {1, 2, 3, 4, 5};
// 将数组转换为流
IntStream intStream = Arrays.stream(numbers);
// 创建一个对象数组
String[] fruits = {"apple", "banana", "orange"};
Stream<String> fruitStream = Arrays.stream(fruits);
intStream.forEach(System.out::println); // 输出整数数组中的元素
fruitStream.forEach(System.out::println); // 输出字符串数组中的元素
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3.3 使用 Stream.of()
创建 Stream
方法介绍:
- 方法:
Stream.of()
- 语法:
public static <T> Stream<T> of(T... values)
- 参数: 接收任意数量的参数,可以是多个对象、字符串等。
- 返回值: 返回一个由这些参数组成的流。
- 作用: 直接通过指定的值创建 Stream,适用于快速创建流的场景。
代码示例:
import java.util.stream.Stream;
public class StreamOfExample {
public static void main(String[] args) {
// 通过 Stream.of() 创建流
Stream<String> stream = Stream.of("apple", "banana", "orange");
stream.forEach(System.out::println); // 顺序输出流中的元素
}
}
2
3
4
5
6
7
8
9
10
# 3.4 创建无限流
方法介绍:
- 方法:
Stream.iterate()
和Stream.generate()
- 语法:
public static <T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static <T> Stream<T> generate(Supplier<T> s)
- 参数:
iterate()
方法接收一个初始值(种子)seed
和一个迭代函数f
。generate()
方法接收一个供应商函数s
,可以无限生成元素。
- 返回值: 返回一个无限流,通常需要通过
limit()
限制流的大小。 - 作用: 创建无限流,用于生成无限序列或随机数据。
代码示例:
import java.util.stream.Stream;
public class InfiniteStreamExample {
public static void main(String[] args) {
// 使用 iterate 生成一个无限流
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);
iterateStream.forEach(System.out::println); // 输出前 10 个偶数
// 使用 generate 生成一个无限流
Stream<Double> generateStream = Stream.generate(Math::random).limit(5);
generateStream.forEach(System.out::println); // 输出 5 个随机数
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4. 流与函数式接口的结合
在流操作中,只要是接收函数式接口的地方,流中的每个元素会自动传递给函数式接口的抽象方法作为参数。这是因为流的操作链条会依次处理每个元素,并将它们传递给下一个操作(比如 map
, filter
, forEach
等)。
换句话说,在流中,只要涉及函数式接口的地方,流会自动将元素传递给函数式接口的抽象方法。这适用于 Lambda 表达式、方法引用以及其他函数式接口的实现。
# 5. Stream 中间操作
中间操作是对流中的数据进行转换、过滤、映射、排序等操作。这些操作是延迟执行的,只有在终止操作时才会触发计算。常见的中间操作包括筛选、映射、排序等。
# 1. filter (条件筛选)
filter
是 Stream API 中的一个中间操作,用于对流中的元素进行条件过滤。它接收一个 Predicate
函数式接口作为参数,Predicate
接口的抽象方法 test(T t)
返回一个布尔值。filter
方法会依次将流中的每个元素传递给这个 Predicate
,只有当 test
方法返回 true
时,该元素才会保留下来,进入到后续的处理阶段。
输入:一个带有条件判断的 Predicate
。
处理:流中的每个元素都会经过这个条件判断。
输出:满足条件(即 test
方法返回 true
)的元素会被保留下来,形成一个新的流。
参数和返回值
- 参数:
Predicate<T>
,用于定义过滤条件,Predicate
的抽象方法是boolean test(T t)
。 - 返回值:返回一个新的
Stream<T>
,只包含满足条件的元素。
代码示例
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
evenNumbers.forEach(System.out::println); // 输出:2, 4, 6, 8, 10
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
详细解析
- Lambda 表达式实现:
n -> n % 2 == 0
实现了Predicate<Integer>
接口的test
方法,判断元素是否为偶数。 - 保留筛选的元素:满足条件的元素被保留下来,形成一个新的流。
- 继续操作:新流可以继续进行其他操作,如映射、排序、收集等。
# 2. map (元素转换)
map
是一个中间操作,用于将流中的每个元素映射为另一种类型。它接收一个 Function
函数式接口作为参数,Function
接口的抽象方法 apply(T t)
接受一个输入参数并返回一个结果。map
方法会将流中的每个元素传递给提供的 Function
函数中的抽象方法 apply(T t)
进行处理,并将转换后的结果作为新的流返回。
输入:一个转换函数 Function
。
处理:对流中的每个元素进行转换。
输出:将转换后的元素放入新的流中。
参数和返回值
- 参数:
Function<? super T, ? extends R>
,用于定义转换逻辑,Function
的抽象方法是R apply(T t)
。 - 返回值:返回一个新的
Stream<R>
,包含转换后的元素。
代码示例
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("hello", "world", "java");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
wordLengths.forEach(System.out::println); // 输出:5, 5, 4
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
详细解析
- Lambda 表达式或方法引用实现:
String::length
是Function<String, Integer>
的实现,用于获取字符串的长度。 - 转换元素类型:将原始流中的每个字符串转换为对应的长度,形成新的流。
- 继续操作:可以对转换后的元素继续进行流操作。
# 3. flatMap (扁平映射)
flatMap
是一个功能更强的映射操作,它将每个元素映射为一个流,然后将所有这些流合并为一个流。它接收一个返回流的 Function
作为参数,flatMap
方法的作用是将嵌套的流结构“展开”成一个平坦的流。
输入:一个返回流的 Function
。
处理:对流中的每个元素进行转换,并将结果展平成一个流。
输出:将展开后的元素放入新的流中。
参数和返回值
- 参数:
Function<? super T, ? extends Stream<? extends R>>
,用于定义扁平映射逻辑,Function
的抽象方法是Stream<R> apply(T t)
。 - 返回值:返回一个扁平化后的
Stream<R>
,包含所有展开后的元素。
代码示例
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class FlatMapExample {
public static void main(String[] args) {
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
Stream<Integer> flatStream = nestedList.stream()
.flatMap(List::stream);
flatStream.forEach(System.out::println); // 输出:1, 2, 3, 4, 5, 6
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
详细解析
- 嵌套结构:
nestedList
是一个包含多个列表的列表。 - 展开操作:
flatMap(List::stream)
将每个列表映射为流,并将所有流合并为一个流。 - 继续操作:新流包含展开后的所有元素,可以继续进行后续操作。
# 4. limit (限制元素数量)
limit
是一个中间操作,用于截取流中的前 N 个元素。它接收一个长整型参数,表示需要保留的元素数量,流中只会保留前 N 个元素,其余的元素会被丢弃。
输入:一个长整型数值 maxSize
。
处理:截取流中的前 N 个元素。
输出:只保留前 N 个元素,形成一个新的流。
参数和返回值
- 参数:
long maxSize
,指定需要保留的元素数量。 - 返回值:返回一个新的
Stream<T>
,只包含前 N 个元素。
代码示例
import java.util.Arrays;
import java.util.List;
public class LimitExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers.stream()
.limit(5) // 只保留前 5 个元素
.forEach(System.out::println); // 输出:1, 2, 3, 4, 5
}
}
2
3
4
5
6
7
8
9
10
11
12
详细解析
- 截取操作:
limit(5)
截取流中的前 5 个元素,后续元素被丢弃。 - 形成新流:返回的新流只包含被保留的元素。
# 5. skip (跳过元素)
skip
是一个中间操作,用于跳过流中的前 N 个元素。它接收一个长整型参数,表示需要跳过的元素数量,流中只保留剩下的元素。
输入:一个长整型数值 n
。
处理:跳过流中的前 N 个元素。
输出:跳过前 N 个元素后的新流。
参数和返回值
- 参数:
long n
,指定需要跳过的元素数量。 - 返回值:返回一个新的
Stream<T>
,只包含剩下的元素。
代码示例
import java.util.Arrays;
import java.util.List;
public class SkipExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers.stream()
.skip(5) // 跳过前 5 个元素
.forEach(System.out::println); // 输出:6, 7, 8, 9, 10
}
}
2
3
4
5
6
7
8
9
10
11
12
详细解析
- 跳过操作:
skip(5)
跳过流中的前 5 个元素,后续元素被保留。 - 形成新流:返回的新流只包含跳过后的剩余元素。
# 6. distinct (去重)
distinct
是一个中间操作,用于去除流中重复的元素。它通过元素的 hashCode()
和 equals()
方法判断元素是否重复,并保留唯一的元素。
输入:无。
处理:根据 hashCode()
和 equals()
去除重复元素。
输出:去除重复
元素后的新流。
参数和返回值
- 参数:无。
- 返回值:返回一个去重后的
Stream<T>
。
代码示例
import java.util.Arrays;
import java.util.List;
public class DistinctExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 5);
numbers.stream()
.distinct() // 去重
.forEach(System.out::println); // 输出:1, 2, 3, 4, 5
}
}
2
3
4
5
6
7
8
9
10
11
12
详细解析
- 去重逻辑:
distinct()
通过元素的hashCode()
和equals()
方法判断元素是否重复。 - 形成新流:返回的新流只包含唯一的元素。
# 7. sorted (排序)
sorted
是一个中间操作,用于对流中的元素进行排序。它有两种形式:一种是自然排序,另一种是自定义排序。
输入:无参数或一个 Comparator
。
处理:根据自然顺序或自定义比较器对元素进行排序。
输出:排序后的新流。
参数和返回值
- 参数:
sorted()
:不接收参数,使用元素的自然顺序进行排序。sorted(Comparator<? super T> comparator)
:接收一个自定义比较器,用于指定排序规则。
- 返回值:返回一个排序后的
Stream<T>
。
代码示例
import java.util.Arrays;
import java.util.List;
public class SortedExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Tom", "Jerry", "Alice", "Bob");
// 自然排序
names.stream()
.sorted() // 使用自然排序
.forEach(System.out::println); // 输出:Alice, Bob, Jerry, Tom
// 自定义排序:根据字符串长度排序
names.stream()
.sorted((s1, s2) -> s1.length() - s2.length()) // 使用自定义排序
.forEach(System.out::println); // 输出:Tom, Bob, Jerry, Alice
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
详细解析
- 自然排序:
sorted()
按照元素的自然顺序(如数值从小到大、字符串按字典顺序)进行排序。 - 自定义排序:
sorted(Comparator)
允许用户指定排序规则,通过比较器实现自定义排序逻辑。
# 6. Stream 终止操作
终止操作是 Stream 处理的最后一步,触发之前的中间操作,生成最终的结果。终止操作通常返回一个值(如 List
、Set
、Optional
等),或产生副作用(如输出、统计、归约等)。在 Stream 执行终止操作后,流会关闭,不能再进行后续操作。
# 1. collect (收集操作)
collect
是一个终止操作,用于将流中的元素收集到一个容器中,如 List
、Set
或 Map
,也可以用来进行字符串拼接。collect
方法接收一个 Collector
,由 Collectors
工具类提供了多种常见的收集器工厂方法,如 toList()
、toSet()
、joining()
等。
输入:一个 Collector
接口的实现(如 Collectors.toList()
)。
处理:将流中的元素按照收集器的规则进行归集。
输出:返回一个收集结果,如 List
、Set
或拼接后的字符串。
参数和返回值
- 参数:
Collector<? super T, A, R>
,用于定义收集规则。 - 返回值:返回一个收集后的结果,类型可以是集合、Map 或字符串等。
代码示例
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class CollectExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "orange", "apple");
// 将流收集为列表
List<String> fruitList = fruits.stream()
.collect(Collectors.toList());
System.out.println("List: " + fruitList);
// 将流收集为集合
Set<String> fruitSet = fruits.stream()
.collect(Collectors.toSet());
System.out.println("Set: " + fruitSet);
// 连接流中的字符串
String joinedFruits = fruits.stream()
.collect(Collectors.joining(", "));
System.out.println("连接后的水果: " + joinedFruits);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
详细解析
Collectors.toList()
:将流中的元素收集到一个List
中。Collectors.toSet()
:将流中的元素收集到一个Set
中,自动去重。Collectors.joining(", ")
:将流中的元素按指定的分隔符拼接成一个字符串。
# 2. reduce (归约操作)
reduce
是一个终止操作,用于将流中的元素通过累积函数组合成一个结果。reduce
适用于求和、求积、求最值等场景。它接收一个二元操作函数和一个可选的初始值。
输入:一个初始值(可选)和一个 BinaryOperator
函数。
处理:依次对流中的元素进行二元运算,产生一个最终的结果。
输出:返回累积后的结果,可能是 Optional<T>
。
参数和返回值
- 参数:
T identity
:初始值(可选)。BinaryOperator<T>
:一个二元操作函数,如加法、乘法等。
- 返回值:返回一个累积结果,可能是
Optional<T>
。
代码示例
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class ReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算所有元素的和
int sum = numbers.stream()
.reduce(0, Integer::sum); // 使用带初始值的 reduce
System.out.println("总和: " + sum);
// 计算所有元素的乘积
Optional<Integer> product = numbers.stream()
.reduce((a, b) -> a * b); // 使用不带初始值的 reduce
product.ifPresent(p -> System.out.println("乘积: " + p));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
详细解析
reduce(0, Integer::sum)
:将初始值设为0
,然后依次将流中的元素累加。reduce((a, b) -> a * b)
:在没有初始值的情况下,依次将流中的元素相乘,结果是一个Optional
。
# 3. forEach (遍历操作)
forEach
是一个终止操作,用于对流中的每个元素进行处理,通常用于输出、打印或其他副作用操作。forEach
接收一个 Consumer
函数式接口,Consumer
的抽象方法是 void accept(T t)
,不返回结果。
输入:一个 Consumer<T>
,用于处理每个元素。
处理:依次对流中的每个元素执行 Consumer
操作。
输出:没有返回值,forEach
仅用于遍历和执行副作用。
参数和返回值
- 参数:
Consumer<T>
,用于定义对每个元素的处理逻辑。 - 返回值:没有返回值,返回类型是
void
。
代码示例
import java.util.Arrays;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "orange");
// 使用 forEach 输出每个元素
fruits.stream()
.forEach(System.out::println); // 输出:apple, banana, orange
}
}
2
3
4
5
6
7
8
9
10
11
12
详细解析
forEach(System.out::println)
:使用方法引用输出流中的每个元素到控制台。- 副作用操作:
forEach
通常用于执行没有返回值的操作,如打印、更新状态等。
# 4. count (计数操作)
count
是一个终止操作,用于返回流中元素的个数。它是一个简单的计数方法,没有参数,返回值是 long
类型。
输入:无参数。
处理:统计流中元素的数量。
输出:返回流中元素的数量,类型为 long
。
参数和返回值
- 参数:无。
- 返回值:
long
,表示流中元素的数量。
代码示例
import java.util.Arrays;
import java.util.List;
public class CountExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "orange");
// 统计流中元素的个数
long count = fruits.stream()
.count(); // 返回流中的元素个数
System.out.println("水果的数量: " + count);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
详细解析
count()
:终止操作,返回流中元素的个数,常用于统计。
# 5. anyMatch, allMatch, noneMatch (匹配操作)
这些是 Stream API 中的短路终止操作,用于判断流中的元素是否满足某些条件。
anyMatch
:只要有一个元素满足条件就返回true
。allMatch
:所有元素都满足条件才返回true
。noneMatch
:没有元素满足条件时返回true
。
输入:一个 Predicate
,用于定义匹配条件。
处理:依次判断流中的每个元素是否满足条件。
输出:返回一个布尔值 true
或 false
。
参数和返回值
- 参数:
Predicate<T>
,用于定义匹配条件。 - 返回值:
boolean
,表示是否有元素满足条件。
代码示例
import java.util.Arrays;
import java.util.List;
public class MatchExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 判断是否存在偶数
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
System.out.println("存在偶数: " + hasEven);
// 判断是否所有元素都大于 0
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0);
System.out.println("所有元素都大于 0: " + allPositive);
// 判断是否没有元素大于 10
boolean noneGreaterThanTen = numbers.stream()
.noneMatch(n -> n > 10);
System.out.println("没有元素大于 10: " + noneGreaterThanTen);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
详细解析
anyMatch(n -> n % 2 == 0)
:只要有一个元素是偶数,结果就为true
。allMatch(n -> n > 0)
:所有元素都
大于 0,结果为 true
。
noneMatch(n -> n > 10)
:没有元素大于 10,结果为true
。
# 6. findFirst, findAny (查找操作)
这两个方法用于在流中查找元素,返回一个 Optional
对象。
findFirst
:返回流中的第一个元素。findAny
:返回流中的任意一个元素,尤其适用于并行流。
输入:无参数。
处理:在流中查找元素。
输出:返回一个 Optional<T>
对象。
参数和返回值
- 参数:无。
- 返回值:
Optional<T>
,表示查找到的元素,可能为空。
代码示例
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class FindExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "orange");
// 查找第一个元素
Optional<String> firstFruit = fruits.stream()
.findFirst();
firstFruit.ifPresent(fruit -> System.out.println("第一个水果: " + fruit));
// 查找任意一个元素
Optional<String> anyFruit = fruits.stream()
.findAny();
anyFruit.ifPresent(fruit -> System.out.println("任意一个水果: " + fruit));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
详细解析
findFirst()
:返回流中的第一个元素,适用于有序流。findAny()
:返回流中的任意元素,在并行流中表现更佳。