《Java8实战》第4章 引入流

news/2024/5/18 17:50:43/文章来源:https://blog.csdn.net/weixin_39924752/article/details/130071137

集合是 Java 中使用最多的 API。

4.1 流是什么

流是 Java API 的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。可以看作是遍历数据集的高级迭代器,而且还可以并行的处理。
例子:返回低热量菜肴名称
Java7的实现

// 用累加器筛选元素
List<Dish> lowCaloricDishes = new ArrayList<>(); 
for(Dish dish: menu) { if(dish.getCalories() < 400) { lowCaloricDishes.add(dish); } 
} 
// 用匿名类对菜肴排序
Collections.sort(lowCaloricDishes, new Comparator<Dish>() { public int compare(Dish dish1, Dish dish2) { return Integer.compare(dish1.getCalories(), dish2.getCalories()); } 
}); 
// 处理排序后的菜名列表
List<String> lowCaloricDishesName = new ArrayList<>(); 
for(Dish dish: lowCaloricDishes) { lowCaloricDishesName.add(dish.getName()); 
} 

上面的代码还有一个“垃圾变量”lowCaloricDishes。
Java8的实现

import static java.util.Comparator.comparing; 
import static java.util.stream.Collectors.toList; 
List<String> lowCaloricDishesName = menu.stream().filter(d -> d.getCalories() < 400) // 选出 400 卡路里以下的菜肴.sorted(comparing(Dish::getCalories)) // 按照卡路里排序.map(Dish::getName) // 提取菜肴的名称.collect(toList()); // 将所有名称保存在 List 中

为了利用多核架构并行执行这段代码,你只需要把 stream()换成 parallelStream()
代码清晰可读。filter 的结果被传给了 sorted 方法,再传给 map 方法,最后传给 collect 方法。流水线一样
image.png

List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), new Dish("beef", false, 700, Dish.Type.MEAT), new Dish("chicken", false, 400, Dish.Type.MEAT), new Dish("french fries", true, 530, Dish.Type.OTHER), new Dish("rice", true, 350, Dish.Type.OTHER), new Dish("season fruit", true, 120, Dish.Type.OTHER), new Dish("pizza", true, 550, Dish.Type.OTHER), new Dish("prawns", false, 300, Dish.Type.FISH), new Dish("salmon", false, 450, Dish.Type.FISH) ); 
Dish 类的定义是:
public class Dish { private final String name; private final boolean vegetarian; private final int calories; private final Type type; public Dish(String name, boolean vegetarian, int calories, Type type) { this.name = name; this.vegetarian = vegetarian; this.calories = calories; this.type = type; } public String getName() { return name; } public boolean isVegetarian() { return vegetarian; } public int getCalories() { return calories; } public Type getType() { return type; } @Override public String toString() { return name; } public enum Type { MEAT, FISH, OTHER } 
} 

接下来会谈到很多模式,比如筛选、切片、查找、匹配、映射和归约。

4.2 流简介

流简短的定义就是“从支持数据处理操作的源生成的元素序列”

  • 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。集合讲的是数据,流讲的是计算。
  • ——流会使用一个提供数据的源,比如集合、数组或 I/O 资源。由列表生成的流,其元素顺序与列表一致。
  • 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,比如 filter、map、reduce、find、match、sort 等。流操作可以顺序执行,也可以并行执行。
  • 流水线——很多流操作本身会返回一个流,这样多个操作就可以链接起来,构成一个更大的流水线。
  • 内部迭代——与集合使用迭代器进行显式迭代不同,流的迭代操作是在后台进行的。
import static java.util.stream.Collectors.toList; 
List<String> threeHighCaloricDishNames = menu.stream() // 从 menu(菜肴列表)获得流.filter(dish -> dish.getCalories() > 300) // 建立操作流水线:首先选出高热量的菜肴.map(Dish::getName) // 获取菜名.limit(3) // 只选择头三个.collect(toList()); // 将结果保存在另一个 List 中
System.out.println(threeHighCaloricDishNames); // 结果是[pork, beef, chicken]

本例先是对 menu 调用 stream 方法,由菜单得到一个流。数据源是菜肴列表(菜单),它给流提供一个元素序列。接下来,对流应用一系列数据处理操作:filter、map、limit 和collect。除了 collect 之外,所有这些操作都会返回另一个流,这样它们就可以接成一条流水线,于是就可以看作对源的一个查询。
最后,collect 操作开始处理流水线,并返回结果(它和别的操作不一样,因为它返回的不是流,在这里是一个 List)。在调用 collect 之前,没有任何结果产生,实际上根本就没有从 menu 里选择元素。你可以这么理解:链中的方法调用都在排队等待,直到调用 collect
filter——接受一个 Lambda,从流中排除某些元素。在本例中,通过传递 Lambda d -> d.getCalories() > 300,选择出热量超过 300 卡路里的菜肴。
map——接受一个 Lambda,将元素转换成其他形式或提取信息。在本例中,通过传递方法引用 Dish::getName,相当于 Lambda d -> d.getName(),提取了每道菜的菜名。
limit——截断流,使其元素不超过给定数量。
collect——将流转换为其他形式。在本例中,流被转换为一个列表。它看起来有点儿像变魔术,第 6 章会详细解释 collect 的工作原理。现在,你可以把 collect 看作能够接受各种方案作为参数,并将流中的元素累积成为一个汇总结果的操作。这里的toList()就是将流转换为列表的方案。
image.png

4.3 流与集合

流就像是一个延迟创建的集合:只有在消费者要求的时候才会计算值
用 DVD 对比在线流媒体的例子展示了流和集合之间的差异。
image.png

4.3.1 只能遍历一次

和迭代器类似,流只能遍历一次。遍历完之后,这个流就已经被消费掉了。
流只能消费一次,多次就会抛异常

List<String> title = Arrays.asList("Modern", "Java", "In", "Action"); 
Stream<String> s = title.stream(); 
s.forEach(System.out::println); 
s.forEach(System.out::println); // 抛异常 java.lang.IllegalStateException:流已被操作或关闭

4.3.2 外部迭代与内部迭代

使用 Collection 接口需要用户去做迭代(比如用 for-each),这称为外部迭代
Stream 库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。
集合:用 for-each 循环外部迭代

List<String> names = new ArrayList<>(); 
for(Dish dish: menu){ // 显式顺序迭代菜单列表names.add(dish.getName()); 
} 

for-each 还隐藏了迭代中的一些复杂性。for-each 结构是一个语法糖,它背后的东西用 Iterator 对象表达出来会更丑陋。
集合:用背后的迭代器做外部迭代

List<String> names = new ArrayList<>(); 
Iterator<String> iterator = menu.iterator(); 
while(iterator.hasNext()) { // 显示迭代Dish dish = iterator.next(); names.add(dish.getName()); 
}

流:内部迭代

List<String> names = menu.stream() .map(Dish::getName) // 用 getName 方法参数化 map,提取菜名.collect(toList()); // 开始执行操作流水线;没有迭代!

比如地上有一堆散落的玩具。需要叫孩子收拾
外部迭代一个集合,显式地取出每个项目再加以处理。我们只需要对孩子说,“把地上所有的玩具都放进盒子里”就好了。
内部迭代比较好的原因有两个:第一,孩子可以选择一只手拿娃娃,另一只手拿球;第二,她可以决定先拿离盒子最近的那个东西,然后再拿别的。
内部迭代时,项目可以透明地并行处理,或者以更优化的顺序进行处理,还可以轻松的并行执行,不需我们管理。
image.png

测验 4.1:外部迭代与内部迭代
基于你对代码清单 4-1 和代码清单 4-2 中外部迭代的学习,请选择一种流操作来重构下面的代码。
List highCaloricDishes = new ArrayList<>();
Iterator iterator = menu.iterator();
while(iterator.hasNext()) {
Dish dish = iterator.next();
if(dish.getCalories() > 300) {
highCaloricDishes.add(d.getName());
}
}
答案:应该选择使用 filter 模式。
List highCaloricDish = menu.stream() .filter(dish -> dish.getCalories() > 300) .collect(toList());

4.4 流操作

filter、map、limit这些都是中间操作,可以连成一条流水线;
collect 触发流水线执行并关闭它。
可以连接起来的流操作称为中间操作,关闭流的操作称为终端操作。
image.png

4.4.1 中间操作

除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。这是因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。
打印流的处理过程

List<String> names = menu.stream() .filter(dish -> { System.out.println("filtering:" + dish.getName()); // 打印当前筛选的菜肴return dish.getCalories() > 300; }) .map(dish -> { System.out.println("mapping:" + dish.getName()); // 提取菜名时打印出来return dish.getName(); }).limit(3) .collect(toList()); 
System.out.println(names); 输出:
filtering:pork 
mapping:pork 
filtering:beef 
mapping:beef 
filtering:chicken 
mapping:chicken 
[pork, beef, chicken] 

尽管很多菜的热量都高于 300 卡路里,但只选出了前三个!这是因为 limit 操作和一种称为短路的技巧,
尽管 filter 和 map 是两个独立的操作,但它们合并到同一次遍历中了(我们把这种技术叫作循环合并

4.4.2 终端操作

终端操作会从流的流水线生成结果,其结果是任何不是流的值,比如 List、Integer,甚至 void。

测验 4.2:中间操作与终端操作
在下列流水线中,你能找出中间操作和终端操作吗?
long count = menu.stream()
.filter(dish -> dish.getCalories() > 300)
.distinct()
.limit(3)
.count();
答案:流水线中最后一个操作 count 返回一个 long,这是一个非 Stream 的值。因此它是一个终端操作。所有前面的操作,filter、distinct、limit,都是连接起来的,并返回一个 Stream,因此它们是中间操作。

4.4.3 使用流

流的使用一般包括三件事:

  • 一个数据源(如集合)来执行一个查询;
  • 一个中间操作链,形成一条流的流水线;
  • 一个终端操作,执行流水线,并能生成结果。

image.png

4.5 路线图

4.6 小结

  • 流是“从支持数据处理操作的源生成的一系列元素”。
  • 流利用内部迭代:迭代通过 filter、map、sorted 等操作被抽象掉了。
  • 流操作有两类:中间操作和终端操作。
  • filter 和 map 等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流水线,但并不会生成任何结果。
  • forEach 和 count 等终端操作会返回一个非流的值,并处理流水线以返回结果。
  • 流中的元素是按需计算的。

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

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

相关文章

Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例

场景 Java中创建线程的方式有三种 1、通过继承Thread类来创建线程 定义一个线程类使其继承Thread类&#xff0c;并重写其中的run方法&#xff0c;run方法内部就是线程要完成的任务&#xff0c; 因此run方法也被称为执行体&#xff0c;使用start方法来启动线程。 2、通过实…

Object方法

系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录对象方法一、Object原型方法1、hasOwnProperty2、isPrototypeOf3、propertyIsEnumerable4、toString5、其他二、Object方法1、assign2、create3、defineProperties4、defineProperty5、…

基于C#编程建立Vector数据类型及对应处理方法

以C#为例&#xff0c;讲解如何建立一个类&#xff0c;这其中需要考虑需要什么样的数据&#xff08;成员&#xff09;&#xff0c;什么样的属性以及方法&#xff0c;以及提供给外部程序调用&#xff0c;最后考虑怎么样去实现这样的算法。例如对于一个向量Vector&#xff08;类&a…

【深度学习】rnn是什么?循环神经网络是什么?RNN前向传播。

文章目录循环神经网络1.循环神经网络原理2.使用Numpy实现RNN层的前向传播3.RNN存在的问题4.小结循环神经网络 通常卷积神经网络 适合处理图像问题&#xff0c;然而通常适合处理自然语言的网络是循环神经网络。rnn是所有基本网络&#xff0c;就像cnn 是很多复杂网络的基本原型。…

leedcode刷题(3)

各位朋友们大家好&#xff0c;今天是我leedcode刷题系列的第三篇&#xff0c;废话不多说&#xff0c;直接进入主题。 文章目录分割链表题目要求用例输入提示做题思路c语言代码实现Java代码实现相交链表题目要求用例输入提示做题思路c语言实现代码Java代码实现分割链表 leedcod…

《 LeetCode 热题 HOT 100》——无重复字符的最长子串

本期给大家带来的是 LeetCode 热题 HOT 100 第三题关于 无重复字符的最长子串 的讲解。首先&#xff0c;我们还是先从题目入手进行分析思考&#xff01;&#xff01;&#xff01; 题目如下 &#xff1a;&#x1f447; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符…

改进蚁狮优化算法

目录 ​1 主要内容 2 部分程序 3 程序结果 4 程序链接 ​1 主要内容 该程序方法复现《改进蚁狮算法的无线传感器网络覆盖优化》两种改进算法模型&#xff0c;即原始ALO算法的基础上添加了两种改进策略&#xff1a; - 改进1&#xff1a;将原先的间断性边界收缩因子变为连…

【Android开发经验】-- 如何实现RecyclerView子项的点击事件?

目录 实例 实现思路 实现代码 进一步需求&#xff1a;数据库存储 实例 假设现在需要完成一个以下需求的任务&#xff0c;下面两个图左边是点击前未完成&#xff0c;右边是点击后已完成&#xff0c;如何实现点击图标切换另一个图标&#xff1f;&#xff08;矩形框中的内容是…

医药产品经理渠道资源获取的方法有哪些?

收集渠道信息是医药产品经理非常重要的工作之一&#xff0c;以下是一些可行的方法&#xff1a; 与销售人员和客户服务团队交流 销售人员和客户服务团队是企业与患者、医生和医院进行联系的主要渠道。他们可以提供很多有关市场需求和竞争对手情况的信息。产品经理可以通过与销…

机械臂动力学参数辨识学习笔记

1、为什么需要动力学参数辨识&#xff1f; 图1 电机三环控制图 通常情况下&#xff0c;标准的工业控制器通过机械臂内部的PID进行调节控制机械臂的运动&#xff0c;即用PID输出力矩&#xff0c;涉及到经典的图一所示的电机三环控制&#xff08;位置环、速度环、电流环&#xff…

用机器学习sklearn+opencv-python过古诗文网4位数字+字母混合验证码

目录 获取验证码图片 用opencv-python处理图片 制作训练数据集 训练模型 识别验证码 编写古诗文网的登录爬虫代码 总结与提高 源码下载 在本节我们将使用sklearn和opencv-python这两个库过掉古诗文网的4位数字字母混合验证码&#xff0c;验证码风格如下所示。 验证码获…

DM的学习心得和知识总结(三)|DM数据库DBMS_WORKLOAD_REPOSITORY 包及其性能分析工具AWR

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、达梦数据库产品及解决方案&#xff0c;点击前往 2、达梦技术文档&#xff0c;点击前往 3、武汉达梦数据库有限公司 官网首页&#xff0c;点击前往 1、本文内容全部…

【软考备战·希赛网每日一练】2023年4月10日

文章目录一、今日成绩二、错题总结第一题第二题三、知识查缺题目及解析来源&#xff1a;2023年04月10日软件设计师每日一练 一、今日成绩 二、错题总结 第一题 解析&#xff1a; 本题属于专业英语&#xff0c;大体了解意思即可。 题目大意&#xff1a; 第二题 解析&#xff1a…

ORACLE创建表空间、用户、授权和Navicat创建序列和触发器及解决ORA-00942、ORA-01219错误

问题描述&#xff1a;因为每次Oracle删除数据库的时候磁盘文件还没删除&#xff0c;然后自己手动停止Oracle&#xff0c;删除磁盘里的.DBF文件导致数据库重启后无法连接。 cmd sqlplus sys as sysdba执行alter database open;查看你报错的数据文件&#xff08;就是你停止Orac…

ESP32 分区表

ESP32 分区表 1. 分区表概述 ESP32 针对 flash 进行划分&#xff0c;划分为不同的区域用作不同的功能&#xff0c;并在flash的 0x8000 位置处烧写了一张分区表用来描述分区信息。 分区表可以根据自己的需要进行配置&#xff0c;每一个分区都有其特定的作用&#xff0c;可根据…

Jetpack Compose之选择器

选择器是啥 选择器主要是指Checkbox复选框&#xff0c;单选开关Switch,滑杆组件Slider等用于提供给用户选择一些值和程序交互的组件&#xff0c;比如像复选框Checkbox&#xff0c;可以让用户选择一个或者多个选项&#xff0c;它可以将一个选项打开或者是关闭&#xff0c;通常用…

【JavaEE】ConcurrentHashMap与Hashtable有什么区别?

博主简介&#xff1a;努力的打工人一枚博主主页&#xff1a;xyk:所属专栏: JavaEE初阶Hashtable、ConcurrentHashMap是使用频率较高的数据结构&#xff0c;它们都是以key-value的形式来存储数据&#xff0c;且都实现了Map接口&#xff0c;日常开发中很多人对其二者之间的区别并…

STM32F4_窗口看门狗精讲(WWDG)

目录 1. 窗口看门狗WWDG简介 2. 窗口看门狗和独立看门狗的区别 3. WWDG主要特性 4. WWDG功能 4.1 窗口看门狗框图(重要) 4.2 看门狗超时计算 5. WWDG寄存器 5.1 控制寄存器 WWDG_CR 5.2 配置寄存器 WWDG_CFR 5.3 状态寄存器 WWDG_SR 6 库函数配置窗口看门狗(采用中断…

Mybatis(五)------Mybatis执行Mapper接口的方法流程

前面几篇文章我们介绍了JDBC、Mybatis的工具类等&#xff0c;下面我们开始对于mybatis的各个机制开始解析。 前面我们知道&#xff0c;mybatis对excutor进行封装成sqlsession提供给开发人员进行数据库的增删改查&#xff0c;我们先从Mybatis最顶层的API入手。 SQLSession的创…

爬虫日常练习-艾图网单页面图片爬取

文章目录爬虫练习分析网站代码设计下载图片完整代码爬虫练习 hello&#xff0c;大家好。好久不见了&#xff0c;无聊的网友今天开始更新关于爬虫的一些日常练习。每次学习完一个新的知识后没有多的案例给自己练习真的很不舒服&#xff0c;希望该系列文章能够让刚刚开始学习爬虫…