详解JAVA Stream

简介: 1.概述Java Stream是Java 8引入的一种新特性,用于简化集合类的数据处理和操作。Stream API提供了一种流式处理的方式,可以对数据进行过滤、映射、聚合等操作,使得数据处理更加简洁、高效和易于理解。stream中对数据的操作分为两类:中间操作,对流的中间操作,即从流中取流。中止操作,对流的最终操作,即从流中取值。两种操作的代码示例:

1.概述

Java Stream是Java 8引入的一种新特性,用于简化集合类的数据处理和操作。Stream API提供了一种流式处理的方式,可以对数据进行过滤、映射、聚合等操作,使得数据处理更加简洁、高效和易于理解。


stream中对数据的操作分为两类:


中间操作,对流的中间操作,即从流中取流。

中止操作,对流的最终操作,即从流中取值。

两种操作的代码示例:

int[] nums={1,2,3};
//map,中间操作,返回的是流
//sum,中止操作,返回的是最终结果
int sum= IntStream.of(nums).map(i->i*2).sum();
System.out.println(sum);

需要注意的一点是:中间操作都是惰性的,中止操作出现,才会真正执行中间操作。


以上面的例子来说,map操作虽然被率先调用了,但是在sum操作调用之前map操作其实是没有被执行的,sum操作调用后map才会执行。

2.流的创建

JDK8开始,JDK中带有多种创建stream的API:

a15c4e9a4d8c4081b035dbd2f133e990.png

//从集合中创建
List<String> list=new ArrayList<>();
list.stream();
list.parallelStream();
//从数组创建
Arrays.stream(new int[]{1,2,3});
//创建数字流
IntStream.of(1,2,3);
//创建无限流(根据传入的参数,截取指定长度)
new Random().ints().limit(10);

3.中间操作

3.1.清单

stream的中间操作,本质上就是对流进行加工,返回的是操作后的stream。


中间操作分为两种:


有状态,依赖于其他中间操作,必须等所依赖的其他中间操作完成后,才可执行。如排序,必须等所有其他还在进行的中间操作完成,才能进行排序。

无状态,不依赖于其他中间操作,自己玩自己的就行。

6c51dcfd766c488990b9c44a72ca1b68.png

3.2.filter

filter(Predicate<T> predicate):根据给定的Predicate函数来过滤Stream中的元素。

List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> result = list.stream().filter(x -> x%2==0).collect(Collectors.toList());
// 输出结果为[2, 4]

3.3.map

map(Function<T, R> mapper):对Stream中的每个元素应用一个函数,返回一个新的Stream对象。

List<String> list = Arrays.asList("apple", "banana", "orange");
List<Integer> result = list.stream().map(x -> x.length()).collect(Collectors.toList());
// 输出结果为[5, 6, 6]

3.4.flatMap

flatMap(Function<T, Stream<R>> mapper):对Stream中的每个元素应用一个函数,将每个元素映射为一个新的Stream对象,然后将所有新的Stream对象合并成一个新的Stream对象。

List<List<Integer>> list = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));
List<Integer> result = list.stream().flatMap(x -> x.stream()).collect(Collectors.toList());
// 输出结果为[1, 2, 3, 4, 5, 6]

3.5.distinct

distinct():去除Stream中的重复元素。

List<Integer> list = Arrays.asList(1, 2, 2, 3, 3, 4, 4, 5);
List<Integer> result = list.stream().distinct().collect(Collectors.toList());
// 输出结果为[1, 2, 3, 4, 5]

3.6.sorted

sorted():对Stream中的元素进行排序。

List<Integer> list = Arrays.asList(4, 2, 1, 3, 5);
List<Integer> result = list.stream().sorted().collect(Collectors.toList());
// 输出结果为[1, 2, 3, 4, 5]

3.7.peek

peek(Consumer<T> action):对Stream中的每个元素应用一个Consumer函数,返回一个新的Stream对象,与map类似,但是它不会改变元素的值。

 List<Integer> list = Arrays.asList(1, 2, 3);
List<Integer> result = list.stream().peek(x -> x = x + 1).collect(Collectors.toList());
// 输出结果为[1, 2, 3]

3.8.limit

limit(long maxSize):截取Stream中的前maxSize个元素。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = list.stream().limit(3).collect(Collectors.toList());
// 输出结果为[1, 2, 3]

3.9.skip

skip(long n):跳过Stream中的前n个元素。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = list.stream().skip(3).collect(Collectors.toList());
// 输出结果为[4, 5]

4.中止操作

4.1.清单

在Java Stream API中,中止操作可以分为短路操作和非短路操作,它们的区别在于短路操作是最后一个操作,后面无法再跟任何操作,非短路操作后面还可以跟其他操作。

f7c580b4ff7b49a38217beb0b2f59a83.png

4.2.非短路操作

4.2.1.forEach

此操作可以迭代Stream中的每个元素,并对其进行操作。

List<String> list = Arrays.asList("Java", "is", "cool");
list.stream()
    .forEach(System.out::println);

4.2.2.count

此操作可以返回Stream中元素的数量。

List<String> list = Arrays.asList("Java", "is", "cool");
long count = list.stream()
                .filter(s -> s.length() > 2)
                .count();
System.out.println(count);

4.2.3.reduce

此操作可以将Stream中的所有元素合并到一个结果中。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = list.stream()
                    .reduce((a, b) -> a + b);
System.out.println(result.get());

4.2.4.collect

此操作可以将Stream中的元素收集到一个集合中。

List<String> list = Arrays.asList("Java", "is", "cool");
List<String> result = list.stream()
                        .collect(Collectors.toList());
System.out.println(result);

4.2.5.max、min

此操作可以返回Stream中的最大或最小元素。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> max = list.stream()
                            .max(Integer::compareTo);
System.out.println(max.get());

4.3.短路操作

4.3.1.findFrist

返回Stream中的第一个元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstEven = numbers.stream()
                                      .filter(n -> n % 2 == 0)
                                      .findFirst();
if (firstEven.isPresent()) {
    System.out.println("The first even number is " + firstEven.get());
} else {
    System.out.println("No even numbers found.");
}

4.3.2.findAny

与findFirst()相似,findAny()也返回Stream中的一个元素,但是这个元素可以是任意一个,而不一定是第一个。在并行Stream中,findAny()可能会返回任何一个符合条件的元素。与findFirst()不同的是,findAny()不保证返回的元素是Stream中的第一个符合条件的元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> anyEven = numbers.stream()
                                    .filter(n -> n % 2 == 0)
                                    .findAny();
if (anyEven.isPresent()) {
    System.out.println("An even number is " + anyEven.get());
} else {
    System.out.println("No even numbers found.");
}

4.3.3.allMatch

allMatch(Predicate<T> predicate):判断Stream中的所有元素是否都满足给定的Predicate条件。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEven = numbers.stream()
                         .allMatch(n -> n % 2 == 0);
if (allEven) {
    System.out.println("All numbers are even.");
} else {
    System.out.println("Some numbers are not even.");
}

4.3.4.anyMatch

anyMatch(Predicate<T> predicate):判断Stream中是否存在任意一个元素满足给定的Predicate条件。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyEven = numbers.stream()
                         .anyMatch(n -> n % 2 == 0);
if (anyEven) {
    System.out.println("At least one number is even.");
} else {
    System.out.println("No even numbers found.");
}

4.3.5.noneMatch

noneMatch(Predicate<T> predicate):判断Stream中是否没有任何元素满足给定的Predicate条件。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noneNegative = numbers.stream()
                              .noneMatch(n -> n < 0);
if (noneNegative) {
    System.out.println("All numbers are non-negative.");
} else {
    System.out.println("Some numbers are negative.");
}

5.并行流

Java Stream提供了一种流式处理的方式,可以快速、方便地对集合、数组等数据进行处理和操作。而并行流则是Java Stream中的一种特殊类型的流,它可以自动将Stream中的数据分成多个小块,并在多个处理器上并行处理这些小块,以提高处理效率。


在Java 8之前,Java只能使用线程来实现并发编程。使用线程编程虽然可以实现并发执行,但是需要程序员自己管理线程的创建、调度和同步等问题,很容易引入死锁、竞争条件等问题。而在Java 8中引入了Stream API和并行流,使得并发编程变得更加容易和高效。


使用并行流的方式非常简单,只需要在创建Stream时调用parallel()方法,就可以将Stream转换为并行流,如下所示:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> result = numbers.parallelStream() // 创建并行流
                               .map(n -> n * n) // 对每个元素进行平方运算
                               .collect(Collectors.toList()); // 将结果收集到列表中
System.out.println(result);

在上面的示例中,使用parallelStream()方法创建一个并行流,并使用map()方法对每个元素进行平方运算,最后使用collect()方法将结果收集到一个列表中。


当列表中的元素数量很大时,使用并行流可以显著提高运行效率,因为并行流可以在多个处理器上并行处理元素。同时,Java Stream API的设计也保证了在并行执行时,Stream的操作是线程安全的,不需要程序员自己进行线程同步。

目录
相关文章
|
3月前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
114 0
|
24天前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
1月前
|
Java 流计算
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
37 1
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
|
1月前
|
Java Shell 流计算
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
24 1
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
|
2月前
|
存储 Java API
Java——Stream流详解
Stream流是JDK 8引入的概念,用于高效处理集合或数组数据。其API支持声明式编程,操作分为中间操作和终端操作。中间操作包括过滤、映射、排序等,可链式调用;终端操作则完成数据处理,如遍历、收集等。Stream流简化了集合与数组的操作,提升了代码的简洁性
84 11
Java——Stream流详解
|
1月前
|
存储 Java 数据处理
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
36 1
|
2月前
|
Java API C++
Java 8 Stream Api 中的 peek 操作
本文介绍了Java中`Stream`的`peek`操作,该操作通过`Consumer&lt;T&gt;`函数消费流中的每个元素,但不改变元素类型。文章详细解释了`Consumer&lt;T&gt;`接口及其使用场景,并通过示例代码展示了`peek`操作的应用。此外,还对比了`peek`与`map`的区别,帮助读者更好地理解这两种操作的不同用途。作者为码农小胖哥,原文发布于稀土掘金。
117 9
Java 8 Stream Api 中的 peek 操作
|
2月前
|
Java C# Swift
Java Stream中peek和map不为人知的秘密
本文通过一个Java Stream中的示例,探讨了`peek`方法在流式处理中的应用及其潜在问题。首先介绍了`peek`的基本定义与使用,并通过代码展示了其如何在流中对每个元素进行操作而不返回结果。接着讨论了`peek`作为中间操作的懒执行特性,强调了如果没有终端操作则不会执行的问题。文章指出,在某些情况下使用`peek`可能比`map`更简洁,但也需注意其懒执行带来的影响。
144 2
Java Stream中peek和map不为人知的秘密
|
2月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
|
2月前
|
Java 程序员 API
Java 8新特性之Lambda表达式与Stream API的探索
【9月更文挑战第24天】本文将深入浅出地介绍Java 8中的重要新特性——Lambda表达式和Stream API,通过实例解析其语法、用法及背后的设计哲学。我们将一探究竟,看看这些新特性如何让Java代码变得更加简洁、易读且富有表现力,同时提升程序的性能和开发效率。