JAVA Stream流

Stream流

是Java 8引入的一种处理数据的强大工具,它提供了一种声明式、高效且易于并行化的编程模型,用于对集合、数组或其他数据源中的元素进行各种计算和操作。Stream API的核心思想是将数据作为一系列元素的序列(流)进行处理,而不是直接操作数据本身。
值得注意的是,流(Stream)是一种基于支持一次性处理数据的数据源的元素序列,流只能使用一次。

以下是对Stream流的详细说明:

核心概念:

数据源(Source) : Stream的起点,可以是集合、数组、I/O通道、生成器函数等,任何能够产生数据序列的源头都可以作为Stream的数据源。
中间操作(Intermediate Operations) : 一系列惰性求值可链接的无状态操作,如filter(过滤)、map(映射)、sorted(排序)、limit(限制数量)、distinct(去重)等。这些操作不会立即执行,而是构建一个流水线式的操作链。中间操作返回一个新的Stream对象,原Stream保持不变。
终端操作(Terminal Operations) : 一个Stream的生命周期以一个终端操作结束,如collect(收集到集合)、forEach(遍历消费)、count(计数)、anyMatch(是否满足条件)、reduce(归约)等。执行终端操作时,会触发中间操作链的执行,从而完成对数据的实际处理。终端操作的结果通常是具体的计算结果或影响(如更新数据库)。
懒加载(Lazy Evaluation) : Stream的中间操作仅定义了如何处理数据,直到遇到终端操作时才开始实际遍历数据源并执行计算。这种延迟执行有助于优化性能,避免不必要的计算。
可并行化(Parallelization): Stream支持并行处理,通过调用parallel()方法可以将串行流转换为并行流,利用多核处理器的优势提高大规模数据处理的效率。并行流会自动划分任务,在多个线程上并行执行中间和终端操作。

主要特点:

声明式编程 : 使用Stream API编写代码时,关注的是"做什么"而非"怎么做"。通过组合各种操作符(方法)来描述数据处理逻辑,代码更简洁、易于理解。
函数式风格 : Stream API鼓励使用lambda表达式和方法引用来定义操作逻辑,符合函数式编程的范式,有利于写出简洁、无副作用的代码。

避免空指针异常: 对于可能为空的集合,Stream API提供了诸如Optional等工具类来优雅地处理空值情况,避免了因空指针引发的运行时异常。
易于优化: Stream的惰性求值和内部优化机制使得JVM和JDK能够在运行时根据具体硬件环境和数据特性进行优化,如减少迭代次数、缓存中间结果等。

典型用法示例:

java 复制代码
List<String> words = Arrays.asList("apple", "banana", "cherry", "date");

例子1:

java 复制代码
// 使用Stream API统计单词列表中长度大于5的单词数量
long count = words.stream()//数据源
                 .filter(word -> word.length() > 5)//中间操作,这是一种lambda表达式的写法
                 .count();//终端操作

例子2:

java 复制代码
// 使用匿名内部类改写Stream API统计单词列表中长度大于5的单词数量
long count = words.stream()
                 .filter(
                 	new Predicate<String>() {//这是一种匿名内部类(老写法),等同于上面的lambda写法
	                     @Override
	                     public boolean test(String word) {
	                         return word.length() > 5;
	                     }
                 	})
                 .count();

例子3:

java 复制代码
// 将单词列表转换为大写并连接成一个字符串
String result = words.stream()//数据源
                     .map(String::toUpperCase)//中间操作(它是Java 8及以后版本引入的lambda表达式的一种简洁表示形式,Java中的方法引用(Method Reference)语法,看例子4)
                     .collect(Collectors.joining(", "));//终端操作

例子4:

java 复制代码
// 将单词列表转换为大写并连接成一个字符串
String result = words.stream()//数据源
                     .map(word -> word.toUpperCase())
                     //中间操作,word是lambda表达式中的参数,word.toUpperCase()则是对参数应用String类的toUpperCase()方法,将其转换为大写。
                     //方法引用String::toUpperCase直接指定了要使用的类(String)和方法名(toUpperCase)
                     .collect(Collectors.joining(", "));//终端操作

例子5:

java 复制代码
// 并行计算单词列表的总长度
int totalLength = words.parallelStream()//数据源(这个是个并行流,下面会讲解,可以理解为将List中的所有String同时进行流处理)
                      .mapToInt(String::length)//中间操作
                      .sum();//终端操作

获取流的常用方式(前三种常用)

1.通过集合获取流:可以使用集合类中的stream()方法或parallelStream()方法来获取流

2.通过数组获取流:可以使用Arrays类中的stream()方法来获取流。

3.通过Stream.of()方法获取流:可以使用Stream类中的of()方法来获取流。

4.通过Stream.iterate()方法获取流:可以使用Stream类中的iterate()方法来获取流
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5); // 获取顺序流

5.通过Stream.generate()方法获取流:可以使用Stream类中的generate()方法来获取流
Stream<Double> stream = Stream.generate(Math::random).limit(5); // 获取顺序流

6.通过Files.lines()方法获取流:可以使用Files类中的lines()方法来获取流。
Stream<String> stream = Files.lines(Paths.get("file.txt")); // 获取顺序流

常用方法用法

Stream API中的中间操作 是指对流进行某种变换或筛选,但并不立即执行操作,而是返回一个新的Stream对象供后续操作链继续构建。中间操作是惰性的,只有当遇到终端操作(如collect、forEach等)时才会触发整个流管道的执行,通常会有多个可链接的无状态操作,譬如在筛选后再映射

以下是常用的Stream中间操作:
1、过滤(Filtering) : filter(Predicate<? super T> predicate) ------ 根据给定的谓词(Predicate)筛选出符合条件的元素。返回一个新的Stream,其中包含原Stream中所有使谓词返回true的元素。

示例:

java 复制代码
   List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
   //筛选所有的偶数
   List<Integer> evenNumbers = numbers.stream()
                                    .filter(n -> n % 2 == 0)
                                    .collect(Collectors.toList());

2、映射(Mapping) : map(Function<? super T, ? extends R> mapper) ------ 将每个元素应用给定的函数(Function),并返回一个新的Stream,其中包含应用函数结果的元素。

示例:

java 复制代码
   List<String> words = Arrays.asList("hello", "world", "java");
   List<Integer> wordLengths = words.stream()
                                   .map(String::length)
                                   .collect(Collectors.toList());
   

3、扁平化(Flattening) : flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) ------ 类似于map,但允许将每个元素转换为一个Stream,然后将所有生成的Stream扁平化为一个单一的Stream。

示例:

java 复制代码
   List<List<String>> nestedWords = Arrays.asList(
       Arrays.asList("hello", "world"),
       Arrays.asList("java", "stream")
   );
   List<String> flattenedWords = nestedWords.stream()
                                           .flatMap(List::stream)
                                           .collect(Collectors.toList());
   

4、排序(Sorting0) : sorted() 或 sorted(Comparator<? super T> comparator) ------ 对流中的元素进行自然排序(对于实现了Comparable接口的类型)或按照指定的比较器(Comparator)进行排序。返回一个新的已排序的Stream。

示例:

java 复制代码
   List<String> words = Arrays.asList("banana", "apple", "cherry");
   List<String> sortedWords = words.stream()
                                   .sorted()
                                   .collect(Collectors.toList());
   

5、切片(Slicing) : limit(long maxSize) 和 skip(long n) ------ 分别限制返回的流最多包含多少个元素(前n个)和跳过前n个元素。这两个操作可以用于分页或取部分数据。

示例:

java 复制代码
   List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
   List<Integer> firstThreeNumbers = numbers.stream()
                                           .limit(3)
                                           .collect(Collectors.toList());

   List<Integer> remainingNumbers = numbers.stream()
                                           .skip(3)
                                           .collect(Collectors.toList());
   

6、去重(Deduplicating) : distinct() ------ 返回一个新的Stream,其中包含原Stream中唯一的元素,即去除重复项。

示例:

java 复制代码
   List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
   List<Integer> uniqueNumbers = numbers.stream()
                                       .distinct()
                                       .collect(Collectors.toList());
   

没有给代码注释和结果,上边的例子已经很好的了解了,建议心写出对应的结果后建一个main函数复制代码进去跑一跑。

这些中间操作可以灵活组合,形成复杂的流处理管道,使得代码更加简洁、易于阅读和维护。中间操作的惰性执行特性有助于提高性能,尤其是在处理大量数据或进行并行计算时。

相关推荐
坐吃山猪1 天前
SpringBoot01-配置文件
java·开发语言
我叫汪枫1 天前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao1 天前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区1 天前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT1 天前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy1 天前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss1 天前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续1 天前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0441 天前
ReAct模式解读
java·ai