网站首页 > 博客文章 正文
Stream API
Java8中有两大最为重要的改变:第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。"集合讲的是数据,流讲的是计算! "
集合和流(Stream),表面上有一些相似之处,他们有不同的目标。集合主要关注其元素的有效管理和访问,相比之下,流不提供直接访问或操纵元素的手段,而是关心声明性地描述其源和将在该源上进行聚合的计算操作。
上篇内容我们学习了创建Stream,接下来我们来看下Stream数据流的消费处理,即中间操作。
Stream的中间操作
为了执行计算,流组合操作被组合成流管道。流管道由源(其可以是数组、集合、生成函数、I/O通道等)组成,零个或多个中间操作(其将流转换成另一个流,例如 filter(Predicate) )以及终端操作(产生结果或副作用,如count()或forEach(Consumer))。
流是惰性的,源数据上的计算仅在终端操作启动时执行,源元素仅在需要时才被使用。
所谓中间操作,即为经过该操作后仍返回Stream的方法,只是(流)数据元素发生了变化。因为仍返回Stream,编程时可以通过连续的多个中间操作,逐渐变为想要的数据流,最后进行终端(终止)操作,变为最终的结果数据。
我们将中间操作分为如下几类:
- 筛选
- 映射
- 排序
以下内容提到XxxStream代表IntStream、LongStream、DoubleStream。如无特殊说明Stream也包含IntStream、LongStream、DoubleStream。
筛选
筛选操作方法即源流经过筛选或过滤后,输出新的数据流。
以下代码见 StreamSearchOperationTest。
distinct的使用
int[] array = new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3};
// count为终端操作,代表统计流中元素个数
log.info("{}数据去重后元素个数为{}个", array, Arrays.stream(array).distinct().count());
filter的使用
log.info("整数3-250中大于等于21,小于148的奇数有{}个",
IntStream.range(3, 251).filter(n -> n >= 21 && n < 148 && n % 2 == 1).count());
limit的使用
log.info("随机生成3-50之间的20个正整数,如下:");
// forEach为终端操作,代表循环消费流中元素
LongStream.generate(() -> RandomUtil.randomLong(3, 50)).limit(20).forEach(System.out::println);
skip的使用
int[] array = new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3};
log.info("{}数据去重头部3个元素后变为:", array);
Arrays.stream(array).skip(3).forEach(System.out::println);
peek的使用
// sum为终端操作,代表流中元素的总和
log.info("以上一串随机浮点数和为:{}",
DoubleStream.generate(RandomUtil::randomDouble).limit(30).peek(System.out::println).sum());
综合示例1
统计如下英文名句的使用的字母个数,要求:忽略大小写,去重、不用重复统计。
根据题目需求,分析实现如下:
- 先将语句转换为小写
- 将语句转换为字符 (Stream) 流
- 过滤出字符流中的字母
- 去除字符流中的重复字母
- 打印字符流中的每个字母
- 输出使用字母个数
最终代码实现如下:
String sentence = "Cease to struggle and you cease to live.(Thomas Carlyle)";
// 正则 \\p{Lower} 小写字母字符:[a-z]
// 正则 \\p{Upper} 大写字母字符:[A-Z]
// 正则 \\p{Alpha} 字母字符:[\\p{Lower}\\p{Upper}]
log.info("语句[{}]使用的字母个数为:{}", sentence,
Arrays.stream(sentence.toLowerCase().split(""))
.filter(str -> str.matches("\\\\p{Alpha}"))
.distinct()
.peek(System.out::println)
.count()
);
综合示例2
在1-3000的整数中,70个数为1组,统计第34组的质数个数,并输出他们。
根据题目需求,实际上该题目为分页模型的变体,取出每页70条第34页的质数,分析实现如下:
- 创建1-3000的整数数字流
- 忽略前33页的整数
- 取出第34页的70个整数
- 过滤为质数的整数
- 打印他们
- 输出使用字母个数
最终代码实现如下:
int page = 34;
int pageNo = 70;
int total = 3000;
log.info("1-{}中每页显示{}条数据,第{}页数据中的质数如下:", total, 70, 34);
// isPrimes 方法判断数字是否是质数(素数)
log.info("质数个数为:{}",
IntStream.range(1, total + 1)
.skip(page * pageNo)
.limit(pageNo)
.filter(NumberUtil::isPrimes)
.peek(System.out::println)
.count()
);
映射
映射操作方法即源流经过转换后,元素类型发生了变化,生成了新的数据流。
Stream的映射方法主要提供了map和flatMap两个方法,二者的区别为map方法的参数生成的是对象,而flatMap方法的参数生成的是Stream,并将这些生成的Stream连接(concat)起来。flatMap类似于对每个流内元素执行map后的结果执行concat方法。可以理解为map方法生成的新Stream流和之前的旧Stream流的元素比为 1:1,而flatMap方法每个元素生成的Stream元素个数为零到多个,最终连接 (concat) 后,新旧元素比为 n:1。
map和flatMap都提供了不同的重载方法,含义一致,下面测试代码不再复述。
以下代码见 StreamMapOperationTest。
map的使用
log.info("1-20的整数每个增加1543后为:");
IntStream.range(1, 21)
.map(n -> n + 1543)
.forEach(System.out::println);
flatMap的使用
// 方式1: 将一个元素变为零个或多个元素组成的Stream
// 需求1: 输出1-20中每个奇数的前后及其本身,并求和
log.info("1-20的中奇数前后元素及求和为:{}",
IntStream.range(1, 21)
.flatMap(n -> n % 2 == 0 ? IntStream.empty() : IntStream.of(n - 1, n, n +1))
.peek(System.out::println)
.sum()
);
// 方式2:将一个元素变为多个元素组成的Stream
// 需求2:
// 生命不止,奋斗不息。(卡莱尔)
String sentence1 = "Cease to struggle and you cease to live.(Thomas Carlyle).";
// 过一种高尚而诚实的生活。当你年老时回想起过去,你就能再一次享受人生。
String sentence2 = "Live a noble and honest life. Reviving past times in your old age will help you to enjoy your life again.";
// 充实今朝,昨日已成过去,明天充满神奇。
String sentence3 = "Enrich your life today. Yesterday is history,and tomorrow is mystery.";
log.info("语句[{}\\n{}\\n{}\\n]使用的字母个数为:{}", sentence1, sentence2, sentence3,
Stream.of(sentence1, sentence2, sentence3)
.flatMap(str -> Arrays.stream(str.toLowerCase().split("")))
.filter(str -> str.matches("\\\\p{Alpha}"))
.distinct()
.peek(System.out::println)
.count()
);
排序
排序操作方法即源流内的元素经过排序后,输出新的数据流。
以下代码见 StreamSortedOperationTest。
sorted的使用
log.info("随机生成3-50之间的20个正整数,排序如下:");
// forEach为终端操作,代表循环消费流中元素
LongStream.generate(() -> RandomUtil.randomLong(3, 50))
.limit(20)
.distinct()
.sorted()
.forEach(System.out::println);
sorted的使用(使用Comparator比较)
log.info("随机生成3-50之间的20个正整数,倒序排序如下:");
// forEach为终端操作,代表循环消费流中元素
Stream.generate(() -> RandomUtil.randomInt(3, 50))
.limit(20)
.distinct()
.sorted(Comparator.comparingInt(o -> -o))
.forEach(System.out::println);
源码详见:https://gitee.com/liuchuang.com/spring-web-flux-study-note
- 上一篇: C#中的for和foreach的探究与学习
- 下一篇: Java foreach语句的用法
猜你喜欢
- 2024-12-26 Java 8 Stream 处理大数据集:实战与优化
- 2024-12-26 面试官:Java8 lambda 表达式 forEach 如何提前终止?
- 2024-12-26 Javascript中,forEach和map到底有什么区别?
- 2024-12-26 Excel VBA之For Each遍历循环的应用
- 2024-12-26 为什么建议使用 for…of 循环而不是 foreach 循环呢
- 2024-12-26 前端开发map和foreach区别,map遍历方式用法介绍
- 2024-12-26 Rust语言从入门到精通系列 - 零基础掌握Stream流迭代器
- 2024-12-26 Map遍历的四种方法效率对比
- 2024-12-26 java集合类之java中集合类有哪些?如何分类?
- 2024-12-26 【一分钟学Java】之List
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)