java8之流的基本使用(二)

概述

流(stream())是java8的一個新特性,主要的做用就是將各類類型的集合轉換爲流,而後的方便迭代數據用的.例如:html

//將List類型的集合轉換爲流
list.stream()

轉換爲流以後能夠進行一系列的迭代操做,比本身去拿出list的值一個個操做要方便的多.java

使用流的好處

  • 聲明性 -- 更簡潔、更易讀
  • 可複合 -- 更靈活
  • 可並行 -- 性能更好

流的使用方法介紹

使用流以前,必須先對函數式接口、lambda表達式和方法引用有一些瞭解,若是您不具有這方面知識,請移駕lambda表達式&方法引用.數組

使用的oracle默認的emp表的字段:oracle

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以上的員工的集合:app

//獲得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方法,能夠看到:this

Stream<T> filter(Predicate<? super T> predicate);

返回的是一個流,參數是一個Predicate.再追這個參數:lua

@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註解說明這是一個函數式接口.並非有了這個註釋才說明這是一個函數式接口,而是只有一個抽象方法的接口,就是函數式接口.code

接口的方法:

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方法將流中全部的元素迭代合成一個結果,能夠求出最大值或最小值
相關文章
相關標籤/搜索