java8之流的基本使用(二)
概述
流(stream())是java8的一个新特性,主要的作用就是将各种类型的集合转换为流,然后的方便迭代数据用的.例如:
//将List类型的集合转换为流
list.stream()
转换为流之后可以进行一系列的迭代操作,比自己去拿出list的值一个个操作要方便的多.
使用流的好处
- 声明性 -- 更简洁、更易读
 - 可复合 -- 更灵活
 - 可并行 -- 性能更好
 
流的使用方法介绍
使用流之前,必须先对函数式接口、lambda表达式和方法引用有一些了解,如果您不具备这方面知识,请移驾lambda表达式&方法引用.
使用的oracle默认的emp表的字段:
public class Emp {
    private BigDecimal empno;
    private boolean trueOrFalse;
    private String ename;
    private String job;
    private BigDecimal mgr;
    private Date hiredate;
    private Double sal;
    private BigDecimal comm;
    private BigDecimal deptno;
    public BigDecimal getEmpno() {
        return empno;
    }
    public void setEmpno(BigDecimal empno) {
        this.empno = empno;
    }
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename == null ? null : ename.trim();
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job == null ? null : job.trim();
    }
    public BigDecimal getMgr() {
        return mgr;
    }
    public void setMgr(BigDecimal mgr) {
        this.mgr = mgr;
    }
    public Date getHiredate() {
        return hiredate;
    }
    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }
    public Double getSal() {
        return sal;
    }
    public void setSal(Double sal) {
        this.sal = sal;
    }
    public BigDecimal getComm() {
        return comm;
    }
    public void setComm(BigDecimal comm) {
        this.comm = comm;
    }
    public BigDecimal getDeptno() {
        return deptno;
    }
    public void setDeptno(BigDecimal deptno) {
        this.deptno = deptno;
    }
}
1.过滤
得到工资在1000以上的员工的集合:
       //得到list集合
        List<Emp> listEmp = empService.listEmp();
        /*
        *  1. listEmp.stream() 将集合转换为流,
        *  这样就可以用流的方法对集合进行迭代.
        *
        *  2.filter方法.里面的emp相当于拿到集合中的每一个emp进行操作,
        *    结果要返回一个Boolean值
        *
        *  3. .collect(toList()),实际是.collect(Collectors.toList()).
        * */
        List<Emp> result = listEmp.stream().filter(emp -> emp.getSal() > 1000).collect(toList());
        System.out.println("result = " + result);
打印输出:
result = [Emp(empno=7499, trueOrFalse=false, ename=ALLEN, job=SALESMAN, mgr=7698, hiredate=Fri Feb 20 00:00:00 CST 1981, sal=1600.0, comm=300, deptno=30), Emp(empno=7521, trueOrFalse=false, ename=WARD, job=SALESMAN, mgr=7698, hiredate=Sun Feb 22 00:00:00 CST 1981, sal=1250.0, comm=500, deptno=30), Emp(empno=7566, trueOrFalse=false, ename=JONES, job=MANAGER, mgr=7839, hiredate=Thu Apr 02 00:00:00 CST 1981, sal=2975.0, comm=null, deptno=20), Emp(empno=7654, trueOrFalse=false, ename=MARTIN, job=SALESMAN, mgr=7698, hiredate=Mon Sep 28 00:00:00 CST 1981, sal=1250.0, comm=1400, deptno=30), Emp(empno=7698, trueOrFalse=false, ename=BLAKE, job=MANAGER, mgr=7839, hiredate=Fri May 01 00:00:00 CST 1981, sal=2850.0, comm=null, deptno=30), Emp(empno=7782, trueOrFalse=false, ename=CLARK, job=MANAGER, mgr=7839, hiredate=Tue Jun 09 00:00:00 CST 1981, sal=2450.0, comm=null, deptno=10), Emp(empno=7788, trueOrFalse=false, ename=SCOTT, job=ANALYST, mgr=7566, hiredate=Sun Apr 19 00:00:00 CDT 1987, sal=3000.0, comm=null, deptno=20), Emp(empno=7839, trueOrFalse=false, ename=KING, job=PRESIDENT, mgr=null, hiredate=Tue Nov 17 00:00:00 CST 1981, sal=5000.0, comm=null, deptno=10), Emp(empno=7844, trueOrFalse=false, ename=TURNER, job=SALESMAN, mgr=7698, hiredate=Tue Sep 08 00:00:00 CST 1981, sal=1500.0, comm=0, deptno=30), Emp(empno=7876, trueOrFalse=false, ename=ADAMS, job=CLERK, mgr=7788, hiredate=Sat May 23 00:00:00 CDT 1987, sal=1100.0, comm=null, deptno=20), Emp(empno=7902, trueOrFalse=false, ename=FORD, job=ANALYST, mgr=7566, hiredate=Thu Dec 03 00:00:00 CST 1981, sal=3000.0, comm=null, deptno=20), Emp(empno=7934, trueOrFalse=false, ename=MILLER, job=CLERK, mgr=7782, hiredate=Sat Jan 23 00:00:00 CST 1982, sal=1300.0, comm=null, deptno=10)]
结果返回了所有工资在1000以上的员工.
分布介绍一下方法,.filter()会对集合中的每一个元素都执行一次括号内的操作:
.filter(emp -> emp.getSal() > 1000)
我们追一下filter方法,可以看到:
Stream<T> filter(Predicate<? super T> predicate);
返回的是一个流,参数是一个Predicate.再追这个参数:
@FunctionalInterface
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
@FunctionalInterface注解说明这是一个函数式接口.并不是有了这个注释才说明这是一个函数式接口,而是只有一个抽象方法的接口,就是函数式接口.
接口的方法:
boolean test(T t);
参数是任意类型,返回值Boolean类型.
也就是说.filter()方法,可以传递任意类型的参数,返回值必须是Boolean类型的,只要满足这两个条件,都可以传到filter方法中.
我们传递的,参数emp,返回值emp.getSal()>1000是个Boolean值,所以满足:
.filter(emp -> emp.getSal() > 1000)
.collect(toList())将结果转换为一个list集合,真正地写法为:
.collect(Collectors.toList());
因为我在这个类中使用了静态导入:
import static java.util.stream.Collectors.*;
所以前面的Collectors可以不写.
2.截断
有时候,得到一个集合只需要其中的几位,这时候可以使用截断:
List<Emp> result = listEmp.stream()
                .filter(emp -> emp.getSal() > 1000)
                //截取3位
                .limit(3)
                .collect(toList());
        System.out.println("result = " + result);
打印输出:result = [Emp(empno=7499, trueOrFalse=false, ename=ALLEN, job=SALESMAN, mgr=7698, hiredate=Fri Feb 20 00:00:00 CST 1981, sal=1600.0, comm=300, deptno=30), Emp(empno=7521, trueOrFalse=false, ename=WARD, job=SALESMAN, mgr=7698, hiredate=Sun Feb 22 00:00:00 CST 1981, sal=1250.0, comm=500, deptno=30), Emp(empno=7566, trueOrFalse=false, ename=JONES, job=MANAGER, mgr=7839, hiredate=Thu Apr 02 00:00:00 CST 1981, sal=2975.0, comm=null, deptno=20)]
.limit()方法,可以截取指定的位数
3.跳过元素
刚刚是截断3位,这次我们输出跳过3位的结果,使用.skip():
 List<Emp> result = listEmp.stream()
                .filter(emp -> emp.getSal() > 1000)
                //跳过3位
                .skip(3)
                .collect(toList());
        System.out.println("result = " + result);
打印输出:result = [Emp(empno=7654, trueOrFalse=false, ename=MARTIN, job=SALESMAN, mgr=7698, hiredate=Mon Sep 28 00:00:00 CST 1981, sal=1250.0, comm=1400, deptno=30), Emp(empno=7698, trueOrFalse=false, ename=BLAKE, job=MANAGER, mgr=7839, hiredate=Fri May 01 00:00:00 CST 1981, sal=2850.0, comm=null, deptno=30), Emp(empno=7782, trueOrFalse=false, ename=CLARK, job=MANAGER, mgr=7839, hiredate=Tue Jun 09 00:00:00 CST 1981, sal=2450.0, comm=null, deptno=10), Emp(empno=7788, trueOrFalse=false, ename=SCOTT, job=ANALYST, mgr=7566, hiredate=Sun Apr 19 00:00:00 CDT 1987, sal=3000.0, comm=null, deptno=20), Emp(empno=7839, trueOrFalse=false, ename=KING, job=PRESIDENT, mgr=null, hiredate=Tue Nov 17 00:00:00 CST 1981, sal=5000.0, comm=null, deptno=10), Emp(empno=7844, trueOrFalse=false, ename=TURNER, job=SALESMAN, mgr=7698, hiredate=Tue Sep 08 00:00:00 CST 1981, sal=1500.0, comm=0, deptno=30), Emp(empno=7876, trueOrFalse=false, ename=ADAMS, job=CLERK, mgr=7788, hiredate=Sat May 23 00:00:00 CDT 1987, sal=1100.0, comm=null, deptno=20), Emp(empno=7902, trueOrFalse=false, ename=FORD, job=ANALYST, mgr=7566, hiredate=Thu Dec 03 00:00:00 CST 1981, sal=3000.0, comm=null, deptno=20), Emp(empno=7934, trueOrFalse=false, ename=MILLER, job=CLERK, mgr=7782, hiredate=Sat Jan 23 00:00:00 CST 1982, sal=1300.0, comm=null, deptno=10)]
4.映射
.map()方法,得到集合中每个元素的某个信息时使用,比如我们只要拿到集合中的所有员工的姓名:
List<Emp> listEmp = empService.listEmp();
        List<String> resultList = listEmp.stream()
                //得到集合中的某一个元素
                .map(emp -> emp.getEname())
                .collect(toList());
        System.out.println("resultList = " + resultList);
打印输出:
resultList = [aaa, SMITH, ALLEN, WARD, JONES, MARTIN, BLAKE, CLARK, SCOTT, KING, TURNER, ADAMS, JAMES, FORD, MILLER]
当你不知道流中的方法,.filter()或者是.map()抑或是其他任何流中的方法里面需要传递什么参数返回什么结果的时候,就可以向之前一样,追踪一下源码,我们以.map()为例:
 <R> Stream<R> map(Function<? super T, ? extends R> mapper);
map方法里面需要的是另一种函数式接口,Function,我们继续追:
@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
可以看到里面的apply方法,是传入任意类型的T,返回的是不同的任意类型的R,在看我们的代码,传入Emp,返回String,满足!:
.map(emp -> emp.getEname())
5.扁平的映射
就是.flatMap()方法.这个方法的作用是将一个流中的每个流都换成另一个值,然后把所有流连接起来成为一个流.
举个具体的例子.将["Hello","World"] 变为["H","e","l","w","r","d"]
List<String> collect = list.stream()
                .map(txt -> txt.split(""))
                .flatMap(txt -> Arrays.stream(txt))
                .distinct().collect(toList());
使用.split("")方法返回两个数组{"H","e","l","l","o"}和{"W","o""r","d"}
.map(txt -> txt.split(""))
Arrays.stream()方法将两个数组变成两个流,flatMap将两个流合成一个流.
.flatMap(txt -> Arrays.stream(txt))
6. 查找和匹配
Stream API提供allMatch、anyMatch、noneMatch、findFirst和findAny方法.判断集合中是否有要匹配的值.
匹配
anyMatch,如果有一个匹配就返回true,看看是否有员工叫SMITH:
 List<Emp> list = empService.listEmp();
        boolean result = list.stream().anyMatch(emp -> emp.getEname().equals("SMITH"));
        System.out.println("result = " + result);
输出结果: result = true
allMatch,全部匹配才返回true:
List<Emp> list = empService.listEmp();
        boolean result = list.stream().allMatch(emp -> emp.getEname().equals("SMITH"));
        System.out.println("result = " + result);
输出: result = false
noneMatch,如果没有匹配到返回true:
List<Emp> list = empService.listEmp();
        boolean result = list.stream().noneMatch(emp -> emp.getEname().equals("SMITH"));
        System.out.println("result = " + result);
打印输出: result = false
anyMatch、allMatch和noneMatch这三个操作都用到了我们所谓的短路,这就是大家熟悉的Java中的&&和||运算符短路在流中的版本.
这里要说明一点,stream分为中间操作和终端操作,像map()、filter()、flatMap()等就是中间操作,他们可以继续调用其它中间操作,想一个流水线一样.而终端操作就是无法再调用其它流的方法的方法,例如 刚刚的三个match方法和collect()方法等.
查找
findAny方法返回当前流中的任意元素,可以将filter和findAny配合使用:
List<Emp> list = empService.listEmp();
        Optional<Emp> result = list.stream().filter(emp -> emp.getSal() > 2000).findAny();
        System.out.println("result = " + result);
        System.out.println("result.get() = " + result.get());
打印输出:
result = Optional[Emp(empno=7566, trueOrFalse=false, ename=JONES, job=MANAGER, mgr=7839, hiredate=Thu Apr 02 00:00:00 CST 1981, sal=2975.0, comm=null, deptno=20)]
result.get() = Emp(empno=7566, trueOrFalse=false, ename=JONES, job=MANAGER, mgr=7839, hiredate=Thu Apr 02 00:00:00 CST 1981, sal=2975.0, comm=null, deptno=20)
细心的朋友可能发现了,这里返回的结果是一个:
Optional<Emp> result
有了Optional类以后就可以和nullPointerException说88了,这是一个容器类,代表一个值存在或不存在
findFirst方法和findAny差不多,但是如果这是一个并行流,findAny返回的就是最先找到的任意一个,而findFirst返回的是第一个.
7. 归约
reduce()方法,求所有员工的工资之和:
List<Emp> list = empService.listEmp();
        Double reduce = list.stream().map(emp -> emp.getSal())
        //这里的第一个参数0代表初始值
                .reduce((double) 0, (a, b) -> a + b);
        System.out.println("得到的工资之和是:"+reduce);
打印输出: 得到的工资之和是:29026.0
注意reduce()方法中的第一个参数0也可以不写但是返回的就是一个Optional类型的结果.
List<Emp> list = empService.listEmp();
        Optional<Double> reduce = list.stream().map(emp -> emp.getSal())
                //这里的第一个参数0代表初始值
                .reduce((a, b) -> a + b);
        System.out.println("得到的工资之和是:"+reduce);
还有一点需要注意的是,如果reduce执行的是乘法,那么初始值就应该是1而不是0.
8.最大值和最小值
使用reduce也可以获得最大值和最小值:
        List<Emp> list = empService.listEmp();
        Optional<Double> reduce = list.stream().map(emp -> emp.getSal())
                //获得最大值
                .reduce(Double::max);
        System.out.println("最大工资是::"+reduce.get());
打印输出: 最大工资是::5000.0
求最小值只需把Double::max换成Double::min.(这是方法引用,看不懂的移步上面的另一篇博客)
9 由值创建流
使用Stream.of方法创建一个流.
Stream<String> values = Stream.of("aaa","bbb","ccc");
        values.map(String::toUpperCase).forEach(System.out::println);
        //等价于下面的
        values.map(value -> value.toUpperCase()).forEach(value -> System.out.println(value));
两个写法的效果是一样的,一个使用的是lambda表达式一个使用的是方法引用.
10. 由数组创建流
int[] values = {1,2,3};
        IntStream stream = Arrays.stream(values);
        //sum方法求和
        int sum = stream.sum();
        System.out.println("sum = " + sum);
打印输出:sum = 6
11.由函数生成流:创建无限流
Stream.iterate和Stream.generate可以创建无穷无尽的流,但是一般会使用limit()方法来限制创建流的大小,以避免打印无数的值,例子:
Stream.iterate(0,n-> n+2)
                .limit(10)
                .forEach(System.out::println);
输出:
0
2
4
6
8
10
12
14
16
18
第一次输出的是0,共输出十次.
generate与iterate类似.
总结
- 可以使用filter、distinct、skip和limit对流做筛选和切片.
 - 使用map和flatMap提取或转换流中的元素.
 - 使用findFirst和findAny方法查找流中的元素.可以用allMatch、noneMatch、anyMatch方法让流匹配给定的谓词.
 - 这些方法都利用了短路:找到结果就立即停止计算;没有必要处理整个流.
 - 使用reduce方法将流中所有的元素迭代合成一个结果,可以求出最大值或最小值
 
java8之流的基本使用(二)的更多相关文章
- 【java多线程】java8的流操作api和fork/join框架
		
原文:https://blog.csdn.net/u011001723/article/details/52794455/ 一.测试一个案例,说明java8的流操作是并行操作 1.代码 package ...
 - 流式计算(二)-Kafka Stream
		
前面说了Java8的流,这里还说流处理,既然是流,比如水流车流,肯定得有流的源头,源可以有多种,可以自建,也可以从应用端获取,今天就拿非常经典的Kafka做源头来说事,比如要来一套应用日志实时分析框架 ...
 - 【转】Java8 Stream 流详解
		
当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...
 - jQuery-瀑布流-绝对定位布局(二)(延迟AJAX加载图片)
		
jQuery-瀑布流-绝对定位布局(二)(延迟AJAX加载图片) 瀑布流-绝对定位布局,与浮动布局的区别在于 1.布局不一样: 绝对定位:一个UL里面放置所有的绝对定位的LI: 浮动布局:多个(一 ...
 - Java8并行流使用注意事项
		
对于从事Java开发的童鞋来说,相信对于Java8的并行流并不陌生,没错,我们常常用它来执行并行任务,但是由于并行流(parallel stream)采用的是享线程池,可能会对我们的性能造成严重影响, ...
 - Android笔记--View绘制流程源码分析(二)
		
Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...
 - GPU编程和流式多处理器(二)
		
GPU编程和流式多处理器(二) 2. 整数支持 SM具有32位整数运算的完整补充. 加法运算符的可选否定加法 乘法与乘法加法 整数除法 逻辑运算 条件码操作 to/from浮点转换 其它操作(例如,S ...
 - 公子奇带你进入Java8流的世界(二)
		
在上一篇中我们带领大家简单的了解流的概念及使用场景,本节我们就来好好的介绍流的常见用法. 一.筛选和切片 对于一串流,我们有时需要取出我们需要的流中某些元素,主要是通过谓词筛选.看代码: 首先定义一个 ...
 - 五分钟学习Java8的流编程
		
1.概述 Java8中在Collection中增加了一个stream()方法,该方法返回一个Stream类型.我们就是用该Stream来进行流编程的: 流与集合不同,流是只有在按需计算的,而集合是已经 ...
 
随机推荐
- java内存基础(一)
			
博客园 闪存 首页 新随笔 联系 管理 订阅 随笔- 35 文章- 0 评论- 29 关于Java 数组内存分配一点认识 //总结:[ 数组引用变量存储在栈内存中,数组对象存储在堆内存当中.数 ...
 - C# winform中PictureBox控件的SizeMode模式
			
SizeMode属性有五种模式, Normal →标准模式, 在此模式下, 图片位于PictureBox的左上角, 图片的大小由PictureBox控件的大小决定, 当图片的大小大于PictureBo ...
 - Linux性能监测:CPU篇
			
CPU 也是一种硬件资源,和任何其他硬件设备一样也需要驱动和管理程序才能使用,我们可以把内核的进程调度看作是 CPU 的管理程序,用来管理和分配 CPU 资源,合理安排进程抢占 CPU,并决定哪个进程 ...
 - js操作window
			
js操作window 常用属性 window.closed // window是否关闭 window.length // iframe个数 window.name // 获取和设置window的名字 ...
 - Maven的安装及配置、Maven在Eclipse中的配置
			
一.需要准备的东西 1. JDK 2. Eclipse 3. Maven程序包 二.检查JAVA安装 三.安装Maven 下载apache-maven-3.5.3-bin.zip解压即可. 四.配置M ...
 - Oracle行转列LISTAGG函数
			
工作过程中需要将查询的数据分组并显示在一行.以往的工作经验,在sql server中可以用for xml path来实现. 现提供Oracle数据库的行转列方式 oracle11g官方文档简介如下: ...
 - Objective-C入门 简介Cocoa框架
			
Cocoa Framework简称Cocoa,它是Mac OS X上的快速应用程序开发(RAD, Rapid Application Development)框架,一个高度面向对象的(Object O ...
 - FastDFS介绍和配置过程 二
			
最近在研究负载均衡和集群,其中涉及到一个主要问题是,如何让集群中的real server共享一套文件系统.在网上查到FastDFS,国人(happy fish,感谢他的开源精神)开发的一套轻量级分 ...
 - Mask_RCNN  caffe2
			
cd DETECTRON/detectron/ python2 tools/infer_simple_ip_camera.py \ --cfg configs/12_2017_baselines ...
 - loj2512 [BJOI2018]链上二次求和
			
传送门 分析 咕咕咕 代码 #include<iostream> #include<cstdio> #include<cstring> #include<st ...