简介
Java 8 引入的 Stream API 是 Java 平台的一个重大改进,它提供了一种高效且表达力强的方式来处理数据集合(如 List、Set)。Stream API 可以让你以声明方式处理数据集合(专注于你要做什么,而不是怎么做),并且可以利用多核处理器的优势进行并行计算。Stream API 是 Java 8 引入的一个功能强大的工具,它提供了一种简洁而高效的方式来处理集合数据。通过使用 Stream,我们可以对集合进行过滤、映射、排序、聚合等操作,而无需编写繁琐的循环和条件语句。Stream 的操作可以串行执行,也可以并行执行,从而提高处理大量数据的效率。
主要特性和优点
- 函数式编程支持:Stream API 充分利用了 Java 8 的 Lambda 表达式和函数式编程概念。
- 声明式数据处理:通过 Stream API,你可以以接近自然语言的方式表达数据处理逻辑,代码更加清晰、简洁。
- 并行处理能力:Stream API 提供了自动的并行处理能力,可以简化并行编程的复杂性,可以自动利用多核处理器,提高处理大量数据的效率。
- 延迟执行:Stream API 的操作是延迟执行的,这意味着它们不会立即执行,而是等到需要结果时才执行。
- 无副作用:Stream API 的操作应该是无副作用的,即它们不会修改数据源。
- 易于集成和扩展:Stream API 可以与 Java 集合框架无缝集成,并且可以很容易地通过自定义的 Spliterator 进行扩展。
基本操作
Stream API 中的操作分为两类:中间操作(Intermediate Operations)和终端操作(Terminal Operations)。
1、中间操作:返回 Stream 本身,因此可以链式调用。中间操作是“惰性”的,即在调用终端操作之前,它们不会执行任何处理。常见的中间操作包括 filter(), map(), sorted(), limit(), skip() 等。
中间操作包括有状态和无状态操作两种:
-
有状态操作:操作需要维护状态来正确执行,每个元素的处理可能依赖于其他元素的状态或上下文。
例如,sorted和distinct操作,是需要维护一个状态,一个是记录位置,一个是记录是否出现过。 -
无状态操作:每个元素的处理都是独立的,不依赖于其他元素的状态。
例如,filter、map和flatMap,只是根据输入元素生成输出元素,而不会受到其他元素的影响。
| 操作类型 | 操作方法 | 描述 |
|---|---|---|
| 中间操作 | filter(Predicate<? super T> predicate) |
过滤流中的元素,只保留满足谓词条件的元素 |
map(Function<? super T, ? extends R> mapper) |
将流中的每个元素映射成另一种形式 | |
flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) |
将流中的每个元素都转换成另一个流,然后将所有流连接成一个流 | |
distinct() |
去除流中的重复元素(基于元素的 equals() 和 hashCode() 方法) |
|
sorted() |
对流中的元素进行自然排序(需要元素实现了 Comparable 接口) |
|
sorted(Comparator<? super T> comparator) |
使用自定义的比较器对流中的元素进行排序 | |
peek(Consumer<? super T> action) |
对流中的每个元素执行操作,但不影响流本身,主要用于调试 | |
limit(long maxSize) |
限制流的元素个数 | |
skip(long n) |
跳过流的前n个元素 |
2、终端操作:返回一个结果或副作用,例如执行一个操作或返回一个非 Stream 的值。终端操作会触发 Stream 管道中所有中间操作的执行。常见的终端操作包括 forEach(), collect(), reduce(), findFirst(), min(), max() 等。
终止操作包括短路操作和非短路操作两种:
- 短路操作:处理元素时,满足某个条件就立即返回结果,无需处理所有元素。
例如,findFirst、findAny、anyMatch和allMatch - 非短路操作:指必须处理所有元素才能得到最终结果;
例如,forEach、reduce和collect
| 操作类型 | 操作方法 | 描述 |
|---|---|---|
| 终止操作 | forEach(Consumer<? super T> action) |
遍历流中的每个元素并执行操作 |
toArray() |
将流中的元素收集到一个数组中(注意:这通常需要一个显式的类型参数,如 toArray(String[]::new)) |
|
reduce(BinaryOperator<T> accumulator) |
归约操作,将流中的元素组合起来,得到一个值(需要元素之间有明确的组合方式) | |
reduce(T identity, BinaryOperator<T> accumulator) |
带初始值的归约操作 | |
collect(Collector<? super T, A, R> collector) |
收集操作,将流中的元素收集到一个集合中,通常与 Collectors 类一起使用 |
|
min(Comparator<? super T> comparator) |
找出流中的最小元素(根据提供的比较器) | |
max(Comparator<? super T> comparator) |
找出流中的最大元素(根据提供的比较器) | |
count() |
计算流中元素的个数 | |
anyMatch(Predicate<? super T> predicate) |
检查流中是否存在至少一个元素满足条件 | |
allMatch(Predicate<? super T> predicate) |
检查流中的所有元素是否都满足条件 | |
noneMatch(Predicate<? super T> predicate) |
检查流中是否不存在任何元素满足条件 | |
findFirst() |
查找流中的第一个元素(返回一个包含单个元素的 Optional) |
|
findAny() |
查找流中的任意一个元素(在并行流中可能不返回第一个元素) |
官网: Stream()官网
一、操作
1、获取流
1.1 从集合(Collection)中获取
@Test
void list() {
// 创建一个List集合
List<String> names = Arrays.asList("John", "Alice", "Bob", "David");
// 获取顺序流
Stream<String> stream = names.stream();
// 使用流进行操作(这里只是示例,没有实际的操作)
stream.forEach(System.out::println);
// 获取并行流
Stream<String> parallelStream = names.parallelStream();
// 使用并行流进行操作(这里只是示例,没有实际的操作)
parallelStream.forEach(System.out::println);
Set<Integer> set = new HashSet<Integer>() {
{
add(1);
add(2);
add(3);
}};
// 通过Set获取
Stream<Integer> stream2 = set.stream();
stream2.forEach(System.out::println);
Map<String, String> map = new HashMap<>();
map.put("map1","test1");
map.put("map2","test2");
// 通过Map.entrySet获取
Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();
stream3.forEach(System.out::println);
// 通过Map.keySet获取
Stream<String> stream4 = map.keySet().stream();
stream4.forEach(System.out::println);
// 通过Map.values获取
Stream<String> stream5 = map.values().stream();
stream5.forEach(System.out::println);
}
1.2 从数组中获取
@Test
void array(){
// 对象数组
String[] strings = {
"Hello", "World", "Java", "Stream"};
Stream<String> stringStream = Arrays.stream(strings);
// 基本类型数组(以int为例)
int[] numbers = {
1, 2, 3, 4, 5};
Stream<Integer> intStream = Arrays.stream(numbers).boxed(); // 基本类型流需要装箱
// 使用流进行操作(这里只是示例,没有实际的操作)
stringStream.forEach(System.out::println);
intStream.forEach(System.out::println);
}
1.3 从其他数据源获取流
@Test
void stream(){
// 使用Stream.of()从多个元素中创建流
Stream<String> stringStream = Stream.of("Apple", "Banana", "Cherry");
// 使用Stream.iterate()创建无限流(这里需要提供一个终止条件来限制流的大小)
Stream<Integer> infiniteIntStream = Stream.iterate(1, n -> n + 1).limit(5); // 生成1到5的流
// 使用Stream.generate()创建无限流(基于提供的Supplier)
Stream<Double> randomStream = Stream.generate(Math::random).limit(10); // 生成10个随机数
stringStream.forEach(System.out::println);
infiniteIntStream.forEach(System.out::println);
randomStream.forEach(System.out::println);
}
1.4 从文件中创建流
@Test
void file(){
// Paths.get("example.txt") 为当前工作目录下名为 example.txt 的文件,没有创建会报错,也可以写绝对路径
try (Stream<String

最低0.47元/天 解锁文章
2772

被折叠的 条评论
为什么被折叠?



