java 流式_Java开发笔记(七十二)Java8新增的流式处理

通过前面几篇文章的学习,大家应能掌握几种容器类型的常见用法,对于简单的增删改和遍历操作,各容器实例都提供了相应的处理方法,对于实际开发中频繁使用的清单List,还能利用Arrays工具的asList方法给清单对象做初始化赋值,另外提供了专门的Collections工具进行排序、求最大元素、求最小元素等操作。那么涉及到更加复杂的数据处理,游荡如何有针对性地筛选和进一步加功能?

依次遍历目标容器,对所有元素逐个加以分析判断,并酌情将具体数据调整至满意的状态,这种千篇一律的业务流程固然能够解决问题,可惜由此带来的副作用是显而易见的,包括但不限于:代码冗长、分支众多、逻辑繁琐、不易重用等等。为了改进相关业务逻辑的编程方式,帮助开发者形成良好的编码风格,Java的每次版本更新都试图给出有效的解决方案,其中影响深远的当数Java8推出的两项新特性:新增的泛型接口与流式处理。关于前一个泛型接口特性,用于容器操作的泛型接口主要有三个,分别是断言接口、消费接口和函数接口,有关的应用案例可参见之前的泛型接口文章,这里不再赘述。真正具有革命性意义的才是本文的主角——流式处理。

所谓流,隐含着流水线的意思,也就是由开发者事先设定一批处理指令,说明清楚每条指令的前因后果,然后启动流水线作业,即可得到最终的处理结果。流式处理的精髓在于一气呵成,只要万事俱备,决不拖泥带水。开展流式处理主要包括三个步骤:获得容器的流对象、设置流的各项筛选和加工指令,以及规划处理结果的展示形式。下面就分别予以详细介绍。

1、获得容器的流对象

Java8给每种容器都准备了两条流水线,一条是串行流,另一条是并行流。串行流顾名思义各项任务是前后串在一起的,只有处理完前一项任务,才能继续执行后一项任务。调用容器实例的stream方法即可获得该容器的串行流对象,而调用容器实例的parallelStream方法可获得该容器的并行流对象。

流对象的获取操作同时也是流式处理的开始指令,每次进行流式处理之前,都必须先获取当前容器的流对象,要么获取串行流,要么获取并行流。

2、设置流的各项筛选和加工指令

不管是串行流还是并行流,它们承载的都是容器内部的原始数据,这些原材料要经过各道加工工序,之后才会得到具备初步形态的半成品。加工数据期间所调用的流方法说明如下:

filter:按照指定条件过滤。即筛选出符合条件的那部分数据。

sorted:根据指定字段对所有记录排序。可选择升序或者降序。

map:映射成指定的数据类型。

limit:只取前面若干条数据。

distinct:去掉重复记录。保证每条记录都是唯一的。

以上的加工方法属于流式处理的中间指令,每次流水线作业都允许设置一条或者多条中间指令。

3、规划处理结果的展示形式

前一步的各项加工处理完毕,还要弄个包装才能输出最终的成品,也就是这条流水线生产出来的数据到底长什么模样。结果数据的记录包装有三种形式,分别对应如下的三个方法:

count:统计结果数据的数量。

forEach:依次遍历结果数据,并逐条进行个性化处理。

collect:搜集和整理结果数据,并返回指定格式的清单记录。

上面的三个包装方法属于流式处理的结束指令,每次流水线作业必须配备有且仅有其中的一条结束指令。

接下来列举几个实际应用的业务场景,看看采取流式处理时该如何编码。首先准备一个原始的苹果清单,后续将对这个苹果清单发动流水作业。原始清单的获取代码示例如下:

// 获取默认的苹果清单

private static ArrayList getAppleList() {

ArrayList appleList = new ArrayList();

appleList.add(new Apple("红苹果", "RED", 150d, 10d));

appleList.add(new Apple("大苹果", "green", 250d, 10d));

appleList.add(new Apple("红苹果", "red", 300d, 10d));

appleList.add(new Apple("大苹果", "yellow", 200d, 10d));

appleList.add(new Apple("红苹果", "green", 100d, 10d));

appleList.add(new Apple("大苹果", "Red", 250d, 10d));

return appleList;

}

然后需要统计红苹果总数的话,可通过下列的流式代码开展统计操作:

// 统计红苹果的总数

long redCount = getAppleList().stream() // 串行处理

.filter(Apple::isRedApple) // 过滤条件。专门挑选红苹果

.count(); // 统计记录个数

System.out.println("红苹果总数=" + redCount);

注意到上述代码的filter方法内部出现了方法引用,的确流式处理的主要方法都预留了函数式接口的调用,所以经常会在流式代码中看到五花八门的方法引用与Lambda表达式。比如下面的结果遍历代码就在forEach方法中填充了Lambda表达式:

// 对每个红苹果依次进行处理

getAppleList().stream() // 串行处理

.filter(Apple::isRedApple) // 过滤条件。专门挑选红苹果

.forEach(s -> System.out.println("当前颜色为"+s.getColor())); // 逐条开展操作

当然流水作业更常见的输出另一串清单数据,此时流式处理的结束指令就得采用collect方法。下面便是从原始清单中挑出红苹果清单的流式代码:

// 挑出红苹果清单

List redAppleList = getAppleList().stream() // 串行处理

//.parallelStream() // 并行处理

.filter(Apple::isRedApple) // 过滤条件。专门挑选红苹果

.sorted(Comparator.comparing(Apple::getWeight)) // 按苹果重量升序排列

//.sorted(Comparator.comparing(Apple::getWeight).reversed()) // 按苹果重量降序排列

.limit(3) // 只取前几条数据

.distinct() // 去掉重复记录

.collect(Collectors.toList()); // 返回一串清单

System.out.println("红苹果清单=" + redAppleList.toString());

结果清单可能不需要完整的苹果信息,只需列出苹果名称字段,那么得调用map方法把完整的苹果信息映射为单个的名称字段。此时的筛选代码变成下面这样:

// 挑出去重后的苹果名称清单

List allNameList = getAppleList().stream() // 串行处理

.map(Apple::getName) // 映射成新的数据类型

.distinct() // 去掉重复记录

.collect(Collectors.toList()); // 返回一串清单

System.out.println("苹果名称去重后的清单=" + allNameList.toString());

除了普通的清单,collect方法还能返回分组清单,也就是把结果数据按照某种条件进行分组,再统计每个分组的成员数目。仍以苹果清单为例,红苹果可通过名称或者产地分组,分组的同时计算每个小组里各有多少粒苹果。于是形成了以下的分组计数代码:

// 按照名称统计红苹果的分组个数

Map redStatisticCount = getAppleList().stream() // 串行处理

.filter(Apple::isRedApple) // 过滤条件。专门挑选红苹果

.collect(Collectors.groupingBy(Apple::getName, Collectors.counting())); // 返回分组计数

System.out.println("红苹果分组计数=" + redStatisticCount.toString());

分组计数仅仅是简单统计各组的成员数量,有时还想单独计算某个字段的统计值,比如每个小组里的苹果总价各是多少?这时collect方法必须同时完成两项任务,第一项要根据某种条件分组,第二项要对各组的苹果价格求和,如此改造之后的分组求和代码如下所示:

// 按照名称统计红苹果的分组总价

Map redPriceSum = getAppleList().stream() // 串行处理

.filter(Apple::isRedApple) // 过滤条件。专门挑选红苹果

.collect(Collectors.groupingBy(Apple::getName, Collectors.summingDouble(Apple::getPrice))); // 返回分组并对某字段求和

System.out.println("红苹果分组总价=" + redPriceSum.toString());

观察以上的具体案例,发现流式处理的代码相当连贯,每个步骤该做什么事情都一清二楚,中间没有许多繁复的流程控制,唯有一条条分工明确的处理指令,同时充分发挥了方法引用及Lambda表达式的便利性,使得原本令人头痛的容器加工变成了有章可循的流水线作业,从而极大地提高了开发者的编码效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/534620.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java保留二位小数_java使double保留两位小数的多方法 java保留两位小数

复制代码代码如下:mport java.text.DecimalFormat;DecimalFormat df new DecimalFormat("######0.00");double d1 3.23456double d2 0.0;double d3 2.0;df.format(d1);df.format(d2);df.format(d3);3个结果分别为:复制代码代码如下:3.230.002.00java保留两位…

linux java jar打包_【Java】Java程序打包成jar包在Linux上运行

当需要把在Windows上开发的Java程序用在Linux上运行时,就需要吧该Java程序打包成jar包上传到Linux上去运行。1.Java程序用MyEclipse打包成可运行的jar包(1)在MyEclipse中选中需要打包的项目,点击右键,选择:Export... 如下图所示&a…

java匿名对象 回收_Java 匿名对象

我们知道一般实例化一个对象的格式,如下:Car car new Car();其中,变量名 car 就是 new Car() 这个对象的名字。car 是引用类型的变量,它的值存放的是对象的引用(或地址),通过 car 这个变量我们就可以间接使用对象。那…

java int 正则表达式_java正则表达式

Java正则表达式正则表达式定义了字符串的模式。正则表达式可以用来搜索、编辑或处理文本。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。Java正则表达式和Perl的是最为相似的。java.util.regex包主要包括以下三个类:Pattern类&#xf…

mysql.h 动态编译命令_Linux环境编译动态库和静态库总结

对Linux环境动态库和静态库的一些基础知识做一些总结,首先总结静态库的编译步骤。1 先基于.cpp或者.c文件生成对应的.o文件2将几个.o文件 使用ar -cr命令 生成libname.a文件libname.a 为静态库, name 为静态库的名字,可以根据模块功能命名。举…

netbeans java中文_Ubuntu 下jdk安装中文字体 java 解决netbeans 方块字 中文乱码

安装环境Ubuntu 11.04、javajdk1.6.0_27首先找到你需要的字体,比如我就是从windows系统里拷出来的,C:\WINDOWS\Fonts这里有很多字体,我只拷贝了simsun.ttc(中文 宋体,从xp系统拷贝的,win7 下没有这个文件)安装java后&a…

python 教学_「Python基础」一次就装好Python手把手装到好

一、前言:安装Python有两个主要的方法,视情况而定我两个都会用:(1)安装 AnacondaAnaconda像一个懒人包,安装它等于把Python安装好连同把Python大部分的套件也下载好了,不只如此连通较常用的Python IDE一同帮你装到好。…

java求二维数组每行的最大值_用JAVA输入一个二维数组a[3][4]的元素值,求输出其元素最大值...

展开全部这个简单啊,把所有元素遍历一边62616964757a686964616fe58685e5aeb931333335343963代码:import java.util.Scanner;public class Help2 {public static void main(String[] args) {Scanner inputnew Scanner(System.in);System.out.print("…

java redis 面试题_Java开发人员怎么面试 常见Redis面试题有哪些

Java开发人员怎么面试?常见Redis面试题有哪些?Redis是目前各大企业都在使用的人们技术,也是企业选拔人才时考核的一个难题。有很多同学只是简单了解Redis的应用,但对于为什么要用Redis以及企业面试中有关Redis的问题却答不上来。接…

java方法重载实事例_零基础java入门教程函数重载function实例化格式案例

java函数的重载,说白了就是函数块函数名一样,但函数类型和参数类型和参数列表个数不同重载之和参数列表有关系,与返回值无关java函数重载函数重载铺垫如下图函数重载铺垫上图功能显示,功能一致所以功能一致所以用的功能函数名一致…

java 类 属性数量_跟我学java编程—Java类的属性与成员变量

在定义类时,经常需要抽象出它的属性,并定义在类的主体中。下面就来介绍与属性相关的内容。常量属性在类中定义的属性有常量属性和成员属性之分。常量属性用final关键字修饰,常量只能赋值一次,在程序中不能修改它的值。一般来说&am…

java获取界面输入数字_通过JAVA设计 GUI 界面的计算器程序,用户可以通过鼠标依次输入参加计算的数值,进行加、减、乘、...

通过JAVA设计 GUI 界面的计算器程序,用户可以通过鼠标依次输入参加计算的数值,进行加、减、乘、2016-08-22 0 0 0 4.0分其他1积分下载如何获取积分?通过JAVA设计 GUI 界面的计算器程序,用户可以通过鼠标依次输入参加计算的数值&am…

java自定义错误码类_如何编写和应用Java的自定义异常类

11.7.1编写自定义异常类的模式编写自定义异常类实际上是继承一个API标准异常类,用新定义的异常处理信息覆盖原有信息的过程。常用的编写自定义异常类的模式如下:public class CustomException extends Exception {//或者继承任何标准异常类public Custom…

java对mysql查询_如何利用java对mysql数据库进行增删改查

代码如下:增:Test//数据插入public void demo1() {Connection connnull;Statement stmtnull;try {//注册驱动Class.forName("com.mysql.jdbc.Driver");//创建连接connDriverManager.getConnection("jdbc:mysql://localhost:3306/name&quo…

util.java_TelnetUtil.java

package com.ailk.ess.webapp2.servermng.net;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import org.apache.commons.net.telnet.TelnetClient;public class TelnetUtil {//telnet客户端对象VT220/VT52Telnet…

生活中java继承例子_简单继承例子:java

通用类,来继承出圆和矩形。package circle;public class Geometric {private String color"white";private boolean filled;private java.util.Date dateCreated;public Geometric(){dateCreatednew java.util.Date();}public Geometric(String color,boo…

java技术简介英文_Java技术常见的英文缩写

1、 URL,Uniform Resource Location,统一资源定位符。2、 JDBC,Java DataBase Connectivity,Java数据库连接。3、 JSP,Java Server Pages,Java服务器页面。4、 JVM,Java Virtual Machine&#x…

Java解决空引用_Java 匠人手法 - 优雅的处理空值

原标题:Java 匠人手法 - 优雅的处理空值作者:Lrwin导语在笔者几年的开发经验中,经常看到项目中存在到处空值判断的情况,这些判断,会让人觉得摸不这头绪,它的出现很有可能和当前的业务逻辑并没有关系。但它会…

mysql 尝试读取超过流末尾的_MySql异常:尝试读取超出流末尾的内容

问题详细描述:2020-09-04 11:28:19,576 [DefaultQuartzScheduler_Worker-1] DEBUG MySql.Data.MySqlClient.MySqlException (0x80004005): Fatal error encountered during command execution. ---> MySql.Data.MySqlClient.MySqlException (0x80004005): Fatal …

mysql对称连接什么意思_对称加密与非对称加密的区别是什么

区别:1、对称加密中加密和解密使用的秘钥是同一个;非对称加密中采用两个密钥,一般使用公钥进行加密,私钥进行解密。2、对称加密解密的速度比较快,非对称加密和解密花费的时间长、速度相对较慢。3、对称加密的安全性相对…