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函数复制代码进去跑一跑。

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

相关推荐
binishuaio3 分钟前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE5 分钟前
【Java SE】StringBuffer
java·开发语言
老友@5 分钟前
aspose如何获取PPT放映页“切换”的“持续时间”值
java·powerpoint·aspose
wrx繁星点点20 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
Upaaui23 分钟前
Aop+自定义注解实现数据字典映射
java
zzzgd81623 分钟前
easyexcel实现自定义的策略类, 最后追加错误提示列, 自适应列宽,自动合并重复单元格, 美化表头
java·excel·表格·easyexcel·导入导出
友善的鸡蛋24 分钟前
解决:使用EasyExcel导入Excel模板时出现数据导入不进去的问题
java·easyexcel·excel导入
星沁城25 分钟前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
NoneCoder37 分钟前
Java企业级开发系列(1)
java·开发语言·spring·团队开发·开发
一只爱好编程的程序猿39 分钟前
Java后台生成指定路径下创建指定名称的文件
java·python·数据下载