在说流之前,我们先来看看集合,为什么呢?作为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流的世界(一)的更多相关文章

  1. 公子奇带你进入Java8流的世界(二)

    在上一篇中我们带领大家简单的了解流的概念及使用场景,本节我们就来好好的介绍流的常见用法. 一.筛选和切片 对于一串流,我们有时需要取出我们需要的流中某些元素,主要是通过谓词筛选.看代码: 首先定义一个 ...

  2. 公子奇带你一步一步了解Java8中Lambda表达式

    在上一篇<公子奇带你一步一步了解Java8中行为参数化>中,我们演示到最后将匿名实现简写为 (Police police) -> "浙江".equals(poli ...

  3. 公子奇带你一步一步了解Java8中行为参数化

    说明:因为本公子一直从事监狱软件开发,所以本系列博客的引入也以此为背景.问题做了简化,只是为了来讲解技术点. 一.问题提出 今日在好好的撸着代码,超哥(民警)找来了,让把监狱30岁以上的民警找给他. ...

  4. 【Java8流】使用学习

    [Java8流]使用学习 ============================================= 1.删除子目录及文件 ============================== ...

  5. Shoot the Bullet(有源汇带上下界最大流)

    有源汇带上下界最大流 在原图基础上连一条汇点到源点流量为inf的边,将有源汇网络流转化为无源汇网络流用相同方法判断是否满流,如果满流再跑一边源点到汇点的最大流就是答案 例题:Shoot the Bul ...

  6. Miox带你走进动态路由的世界——51信用卡前端团队

    写在前面: 有的时候再做大型项目的时候,确实会被复杂的路由逻辑所烦恼,会经常遇到权限问题,路由跳转回退逻辑问题.这几天在网上看到了51信用卡团队开源了一个Miox,可以有效的解决这些痛点,于是乎我就做 ...

  7. 一文带你了解 C# DLR 的世界

    一文带你了解 C# DLR 的世界 在很久之前,我写了一片文章dynamic结合匿名类型 匿名对象传参,里面我以为DLR内部是用反射实现的.因为那时候是心中想当然的认为只有反射能够在运行时解析对象的成 ...

  8. MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界

    MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...

  9. JAVA8给我带了什么——并行流和接口新功能

    流,确定是笔者内心很向往的天堂,有他之后JAVA在处理数据就变更加的灵动.加上lambda表达不喜欢都不行.JAVA8也为流在提供另一个功能——并行流.即是有并行流,那么是不是也有顺序流.没有错.我前 ...

随机推荐

  1. oracle函数 NLS_LOWER(x[,y])

    [功能]返回字符串并将字符串的变为小写; [参数]x字符型表达式 [参数]Nls_param可选,指定排序的方式(nls_sort=) . SCHINESE_RADICAL_M(部首.笔画) SCHI ...

  2. 30 Cool Open Source Software I Discovered in 2013

    30 Cool Open Source Software I Discovered in 2013 #1 Replicant – Fully free Android distribution Rep ...

  3. 微软产品开发文档:包括.net core .net vs等等

    Browse all https://docs.microsoft.com/en-us/learn/browse/?roles=developer&products=xamarin%2Cef- ...

  4. logback 打印mybatis sql mybatis 日志打印sql语句和返回结果

    logback 打印sql语句: 在logback日志文件中开启debug模式 <logger name="com.ibatis" level="DEBUG&quo ...

  5. H3C 启动包过滤防火墙功能

  6. MFC/Win32里面调用qtwebkit

    可以用qtwinmigrate 文档在:http://doc.qt.digia.com/solutions/4/qtwinmigrate/index.html 下载在:https://qt.gitor ...

  7. H3C 多路径网络中环路避免操作示例

  8. H3C IPv6地址解析

  9. CCPC 2018 吉林 C "JUSTICE" (数学)

    传送门 参考资料: [1]:https://blog.csdn.net/mmk27_word/article/details/89789770 题目描述 Put simply, the Justice ...

  10. redux.js的基本使用

    1.先是安装reduxJx, cnpm i --save rudux 2.创建一个store的js文件 3.使用import来引用 redux import { createStore } from ...