公子奇带你进入Java8流的世界(一)
在说流之前,我们先来看看集合,为什么呢?作为Java8中的新成员,它和集合有很多相似之处,同时它们也是可以互相转化的。集合不仅仅是Java语言,任何一门高级开发语言都有集合的概念,集合顾名思义,就是很多数据集放在一起,它算是一个容器,同时我们也可以使用泛型来限制集合中的数据类型。
一、流是什么
流作为是Java8的新成员,它允许我们以声明性的方式来处理数据集合。我们可以把它看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,这就使得我们无需编写任何多线程代码。在后续中我们再来详细说说流和流的并行化。
话不多说,我们用一段代码来直观比较Java8前后的区别:
package com.hz; import java.util.*; import static java.util.stream.Collectors.toList; public class StreamDemo {
public static void main(String[] args) {
List<Police> polices = Arrays.asList(
new Police("P001", "余警官", 27, "浙江"),
new Police("P002", "李警官", 32, "安徽"),
new Police("P003", "程警官", 25, "安徽"),
new Police("P004", "杨警官", 35, "浙江"),
new Police("P005", "张警官", 31, "上海"),
new Police("P006", "王警官", 42, "浙江"),
new Police("P007", "赵警官", 31, "浙江"),
new Police("P008", "刘警官", 49, "浙江"),
new Police("P009", "周警官", 32, "浙江")
); //问题:将以上集合中的民警,大于30岁的民警筛选出来,并按照年龄排序,将筛选结果的民警姓名存储到另一个集合中 /*********************************** Java7实现以上问题 ************************************************/
List<Police> tempList = new ArrayList<>();
for (Police police : polices) {
if (police.getPoliceAge() > 30) {
tempList.add(police);
}
}
Collections.sort(tempList, new Comparator<Police>() {
@Override
public int compare(Police o1, Police o2) {
return Integer.compare(o1.getPoliceAge(), o2.getPoliceAge());
}
});
List<String> tempPoliceNameList = new ArrayList<>();
for (Police police : tempList) {
tempPoliceNameList.add(police.getPoliceName());
}
System.out.println(tempPoliceNameList); System.out.println("-------------------------------- 分割线 ---------------------------------------"); /*********************************** Java8实现以上问题 ************************************************/
List<String> tempPoliceNameStream = polices.stream().filter(p -> p.getPoliceAge() > 30)
.sorted(Comparator.comparing(Police :: getPoliceAge))
.map(Police :: getPoliceName)
.collect(toList());
System.out.println(tempPoliceNameStream); //说明:若想并行执行,在Java8中,只需要将polices.stream() 改为polices.parallelStreamm()
}
} 结果:
[张警官, 赵警官, 李警官, 周警官, 杨警官, 王警官, 刘警官]
-------------------------------- 分割线 ---------------------------------------
[张警官, 赵警官, 李警官, 周警官, 杨警官, 王警官, 刘警官]
从以上一段代码中,我们可以看出:
1、代码是以声明性方式编写。即想要完成的工作,而非如何完成。
2、可以使用操作链。filter之后的方法可以直接点,直到完成。
由上,我们可以先简单的总结下使用流的好处:
1、声明性:更简洁易读。
2、可复用:更加灵活。
3、可并行:性能更好。
二、流的介绍
上面我看了流和集合的简单比较,那么到底流是什么呢?我们可以简单说明为“从支持数据处理操作的源生成的元素序列”。我们将这句话分开来解析:
①、元素序列:它就如何集合一样,可以访问特定元素类型的一组有序值。但它与集合是不同的,集合是一种数据结构,它的主要目的是在一定时间和空间上存储数据。而流主要用来计算。他们本质上是不同的。
②、源:即源头,流在处理数据时,这个数据的源头,例如:集合可以是个源,文件也可以是个源。
③、数据处理操作:流在处理数据时类似我们操作数据库,如:filter/map/sort等。流在处理数据时,可顺序执行也可并行执行。
流在操作中具有两个很明显的特征:
1、流水线。即流的操作返回的还是一个流,如此多个操作就可以一直往后链接,从而形成一个流水线。
2、内部迭代。流在处理时,我们是看不到处理过程的,它是在背后执行的。我们可以回看上一节中,民警在筛选/排序/映射到后面的截取/转换等如何完成的,我们无法看到执行过程。
三、集合与流比对
在Java8中集合和流是可以互相转化的,但从数据上来看,集合是可以不断的遍历,而流只可以遍历一次,一次遍历结束后,即代表该条流完成,若想再次处理,则需要重新建立一个流对象。若我们对一个已经完成的流再次处理,则会抛出异常。
package com.hz; import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream; public class StreamDemo2 {
public static void main(String[] args) {
List<String> list = Arrays.asList("Java", "JavaScript", "Python");
Stream<String> stream = list.stream();
stream.forEach(System.out :: println); System.out.println("------ 分割线 --------"); stream.forEach(System.out :: println);
}
} 结果:
Java
JavaScript
Python
------ 分割线 --------
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.hz.StreamDemo2.main(StreamDemo2.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
根据结果我们可知道,流已经被操作完或关闭了。当我们想再操作该流时,则异常。
我们上一节说到流是内部迭代的,集合也是有迭代的,只是集合一般我们都是外部迭代,那么这两种迭代方式有什么不同呢?
package com.hz; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List; import static java.util.stream.Collectors.toList; public class StreamDemo3 {
public static void main(String[] args) {
List<Book> books = Arrays.asList(
new Book("Java入门", "张**"),
new Book("JavaScript入门", "李**"),
new Book("Python入门", "王**")
); List<String> booksName = new ArrayList<>();
//************ 1- 使用for-each做外部迭代
for (Book book : books) {
booksName.add(book.getBookName());
}
System.out.println(booksName); System.out.println("--------- 分割线 ---------");
booksName.clear();
//************ 2- 使用迭代器(背后迭代器)做外部迭代
Iterator<Book> bookIterator = books.iterator();
while (bookIterator.hasNext()) {
Book book = bookIterator.next();
booksName.add(book.getBookName());
}
System.out.println(booksName); System.out.println("--------- 分割线 ---------");
booksName.clear();
//************ 1- 使用流 内部迭代
booksName = books.stream().map(Book :: getBookName).collect(toList());
System.out.println(booksName);
} static class Book {
private String bookName;
private String bookAuth; public String getBookName() {
return bookName;
} public void setBookName(String bookName) {
this.bookName = bookName;
} public String getBookAuth() {
return bookAuth;
} public void setBookAuth(String bookAuth) {
this.bookAuth = bookAuth;
} public Book(String bookName, String bookAuth) {
this.bookName = bookName;
this.bookAuth = bookAuth;
}
}
} 说明:由上代码我们可以看到1/2两种迭代的过程我们是知道的,而3我们无法看到迭代细节。
四、流基本操作介绍
从上一节,我们可以看到一段代码
polices.stream().filter(p -> p.getPoliceAge() > 30) .sorted(Comparator.comparing(Police :: getPoliceAge)) .map(Police :: getPoliceName) .collect(toList());
对于这段代码我们可以将其分为两个部分或两种操作:
1、从stream方法之后,filter / sorted / map 返回的都为一个流,组成一个流水线。
2、collect方法执行后流即关闭,并返回一个非流对象。
对于这两个部分,我们将第一种操作称为中间操作,第二种操作称为终端操作。
在中间操作中,代码的真正逻辑并没有执行,只有当遇到了终端操作,之前的中间操作才开始执行,知道结束并关闭流。
由这些,我们也可以以总结下流的使用主要包含了三件事,即首先需要一个数据源用来执行查询,再次需要一个中间操作链形成一条流的流水线,最后需要一个终端操作来执行流水线并返回结果。
公子奇带你进入Java8流的世界(一)的更多相关文章
- 公子奇带你进入Java8流的世界(二)
在上一篇中我们带领大家简单的了解流的概念及使用场景,本节我们就来好好的介绍流的常见用法. 一.筛选和切片 对于一串流,我们有时需要取出我们需要的流中某些元素,主要是通过谓词筛选.看代码: 首先定义一个 ...
- 公子奇带你一步一步了解Java8中Lambda表达式
在上一篇<公子奇带你一步一步了解Java8中行为参数化>中,我们演示到最后将匿名实现简写为 (Police police) -> "浙江".equals(poli ...
- 公子奇带你一步一步了解Java8中行为参数化
说明:因为本公子一直从事监狱软件开发,所以本系列博客的引入也以此为背景.问题做了简化,只是为了来讲解技术点. 一.问题提出 今日在好好的撸着代码,超哥(民警)找来了,让把监狱30岁以上的民警找给他. ...
- 【Java8流】使用学习
[Java8流]使用学习 ============================================= 1.删除子目录及文件 ============================== ...
- Shoot the Bullet(有源汇带上下界最大流)
有源汇带上下界最大流 在原图基础上连一条汇点到源点流量为inf的边,将有源汇网络流转化为无源汇网络流用相同方法判断是否满流,如果满流再跑一边源点到汇点的最大流就是答案 例题:Shoot the Bul ...
- Miox带你走进动态路由的世界——51信用卡前端团队
写在前面: 有的时候再做大型项目的时候,确实会被复杂的路由逻辑所烦恼,会经常遇到权限问题,路由跳转回退逻辑问题.这几天在网上看到了51信用卡团队开源了一个Miox,可以有效的解决这些痛点,于是乎我就做 ...
- 一文带你了解 C# DLR 的世界
一文带你了解 C# DLR 的世界 在很久之前,我写了一片文章dynamic结合匿名类型 匿名对象传参,里面我以为DLR内部是用反射实现的.因为那时候是心中想当然的认为只有反射能够在运行时解析对象的成 ...
- MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界
MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...
- JAVA8给我带了什么——并行流和接口新功能
流,确定是笔者内心很向往的天堂,有他之后JAVA在处理数据就变更加的灵动.加上lambda表达不喜欢都不行.JAVA8也为流在提供另一个功能——并行流.即是有并行流,那么是不是也有顺序流.没有错.我前 ...
随机推荐
- HZOJ 通讯
B. 通讯 题目描述 “这一切都是命运石之门的选择.” 试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短 信,并由此得知了伦太郎制作出了电话微波炉(仮). 为了掌握时间机器的技术, ...
- H3C 命令行帮助特性
- uniapp点击底部tabbar不跳转页面
一个项目,其设想是这样的,当我进入页面,发现有新版本,提示用户之后,用户点击确定跳转到下载页面. 弹出框要用自己封装的,因为uniapp的弹出框不同的手机上展示的样子不一样,领导的是华为(在这里悄悄吐 ...
- Android Studio(一):介绍、安装、配置
Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...
- input 的 pattern 验证表单
pattern 用于定义验证输入正则表达式 pattern 属性适用于以下 <input> 类型:text, search, url, telephone, email 以及 passwo ...
- Java反射机制(四):动态代理
一.静态代理 在开始去学习反射实现的动态代理前,我们先需要了解代理设计模式,那何为代理呢? 代理模式: 为其他对象提供一种代理,以控制对这个对象的访问. 先看一张代理模式的结构图: 简单的理解代理设计 ...
- POJ 2763"Housewife Wind"(DFS序+树状数组+LCA)
传送门 •题意 一对夫妇居住在 xx村庄,给村庄有 $n$ 个小屋: 这 $n$ 个小屋之间有双向可达的道路,不会出现环,即所构成的图是个树: 从 $a_i$ 小屋到 $b_i$ 小屋需要花费 $w_ ...
- es6—变量的解构赋值
数组的解构赋值 ]]]]]]] = []}} = {}} = {}})]: first]: last} = arr} = {}) {}))}))}) {}))}))].]]]])})] }}} = { ...
- java编译器优化和运行期优化
概述 最近在看jvm优化,总结一下学习的相关知识 (一)javac编译器 编译过程 1.解析与填充符号表过程 1).词法.语法分析 词法分析将源代码的字符流转变为标记集合,单个字符是程序编 ...
- 微信里首次跳转会到首页问题(window.location失效)
将window.location.href 换为location.href