通过前面几篇文章的学习,大家应能掌握几种容器类型的常见用法,对于简单的增删改和遍历操作,各容器实例都提供了相应的处理方法,对于实际开发中频繁使用的清单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表达式的便利性,使得原本令人头痛的容器加工变成了有章可循的流水线作业,从而极大地提高了开发者的编码效率。

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

  1. java swing输入框_Java开发笔记(一百二十九)Swing的输入框

    Swing的输入框仍然分成两类:单行输入框和多行输入框,但与AWT的同类控件相比,它们在若干细节上有所调整.首先说单行输入框,AWT的单行输入框名叫TextField,平时输入什么字符它便显示什么字符 ...

  2. 【Visual C++】游戏开发笔记四十二 浅墨DirectX教程之十 游戏输入控制利器 DirectInput专场

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  3. 【Visual C++】游戏开发笔记四十二 浅墨DirectX教程之十 游戏输入控制利器:DirectInput专场...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/zhmxy555/article/details/8547531 作者:毛星云(浅墨) ...

  4. 【Visual C++】游戏开发笔记四十二 浅墨DirectX教程之十 游戏输入控制利器:DirectInput专场

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8547531 作者:毛星云(浅墨 ...

  5. 【Visual C++】游戏开发笔记三十二 浅墨DirectX提高班之一 DirectX大局观认知篇

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:  http://blog.csdn.net/zhmxy555/article/details/8172615 作者:毛星云(浅 ...

  6. Android开发笔记(十二)测量尺寸与下拉刷新

    尺寸测量的配置 控件宽和高的设置方式 大家知道,自定义视图的目的就是要在屏幕上显示期望的图案,那在绘制图案之前,我们得先知道这个图案的尺寸(如宽多少高多少). 一般在xml中给控件的宽和高有三种赋值方 ...

  7. java 文件缓冲区_Java开发笔记(八十六)通过缓冲区读写文件

    前面介绍了利用文件写入器和文件读取器来读写文件,因为FileWriter与FileReader读写的数据以字符为单位,所以这种读写文件的方式被称作"字符流I/O",其中字母I代表输 ...

  8. java 日历工具_Java开发笔记(四十二)日历工具的常见应用

    前面介绍了日历工具Calendar的基本用法,乍看起来Calendar与Date两个半斤八两,似乎没有多大区别,那又何苦庸人自扰鼓捣一个新玩意呢?显然这样小瞧了Calendar,其实它的作用大着呢,接 ...

  9. java文本输入框_Java开发笔记(一百三十九)JavaFX的输入框

    循着Swing的旧例,JavaFX仍然提供了三种文本输入框,分别是单行输入框TextField.密码输入框PasswordField.多行输入框TextArea.这些输入框都由抽象类TextInput ...

最新文章

  1. Lintcode 988解题思路和c++代码
  2. 李宏毅深度学习作业二
  3. 字符与字符串3——char 的大小
  4. [知了堂学习笔记]_网络基础知识_1.OSI参考模型(网络七层协议)
  5. 利用application在页面中显示访问次数
  6. android播放音频的格式,android 音频播放_android ios 音频格式_android 播放网络音频...
  7. android svn新建分支,android studio中创建、切换svn分支
  8. svn与git的区别(总结)
  9. 本科双非,考研压线上北大,总结一下我的复习过程,希望对考研er有点帮助!
  10. 缅怀过往_飘云羽逸_新浪博客
  11. tkinter 动态显示时间的方法
  12. 转化为五分制的c语言程序,用C语言把百分制转化为五分制
  13. 通过 irp 请求包删除文件
  14. 用浏览器下载一个文件,当点击该文件(原理)
  15. python截取图片中的圆形区域
  16. 链路聚合,链路聚合是什么意思
  17. 没基础不用怕,零基础无需任何技术学重装电脑系统
  18. Flex 之旅(一)
  19. KIEL编译程序中的KEIL与ERROR
  20. 自动控制原理模拟卷5

热门文章

  1. 如何给U盘设置一张妖娆又骚气的图标
  2. vue怎么获取用户的位置经纬度_vue 实现Web端的定位功能 获取经纬度
  3. 最大值_Leetcode2 | 滑动窗口最大值(Q239)
  4. 怎么从gitlab上下载别人的代码
  5. linux上怎么解压zip文件和tar.gz文件
  6. BugkuCTF-MISC题telnet
  7. oracle查询删除时间戳,Oracle查询时间戳,建表时间,新建序列,误删数据
  8. 分享到facebook没有封面图_拾柒自制书封面图分享~
  9. linux启动tongweb命令,linux7开机自启动东方通tongweb
  10. 用python画风车_python接口自动化()一)(实现一款简单的接口自动化框架)