公子奇帶你進入Java8流的世界(一)

    在說流以前,咱們先來看看集合,爲何呢?做爲Java8中的新成員,它和集合有不少類似之處,同時它們也是能夠互相轉化的。集合不只僅是Java語言,任何一門高級開發語言都有集合的概念,集合顧名思義,就是不少數據集放在一塊兒,它算是一個容器,同時咱們也可使用泛型來限制集合中的數據類型。java

1、流是什麼

    流做爲是Java8的新成員,它容許咱們以聲明性的方式來處理數據集合。咱們能夠把它當作遍歷數據集的高級迭代器。此外,流還能夠透明地並行處理,這就使得咱們無需編寫任何多線程代碼。在後續中咱們再來詳細說說流和流的並行化。數據庫

    話很少說,咱們用一段代碼來直觀比較Java8先後的區別:數據結構

 1 package com.hz;
 2 
 3 import java.util.*;
 4 
 5 import static java.util.stream.Collectors.toList;
 6 
 7 public class StreamDemo {
 8     public static void main(String[] args) {
 9         List<Police> polices = Arrays.asList(
10                 new Police("P001", "餘警官", 27, "浙江"),
11                 new Police("P002", "李警官", 32, "安徽"),
12                 new Police("P003", "程警官", 25, "安徽"),
13                 new Police("P004", "楊警官", 35, "浙江"),
14                 new Police("P005", "張警官", 31, "上海"),
15                 new Police("P006", "王警官", 42, "浙江"),
16                 new Police("P007", "趙警官", 31, "浙江"),
17                 new Police("P008", "劉警官", 49, "浙江"),
18                 new Police("P009", "周警官", 32, "浙江")
19         );
20 
21         //問題:將以上集合中的民警,大於30歲的民警篩選出來,並按照年齡排序,將篩選結果的民警姓名存儲到另外一個集合中
22 
23         /*********************************** Java7實現以上問題 ************************************************/
24         List<Police> tempList = new ArrayList<>();
25         for (Police police : polices) {
26             if (police.getPoliceAge() > 30) {
27                 tempList.add(police);
28             }
29         }
30         Collections.sort(tempList, new Comparator<Police>() {
31             @Override
32             public int compare(Police o1, Police o2) {
33                 return Integer.compare(o1.getPoliceAge(), o2.getPoliceAge());
34             }
35         });
36         List<String> tempPoliceNameList = new ArrayList<>();
37         for (Police police : tempList) {
38             tempPoliceNameList.add(police.getPoliceName());
39         }
40         System.out.println(tempPoliceNameList);
41 
42         System.out.println("-------------------------------- 分割線 ---------------------------------------");
43 
44         /*********************************** Java8實現以上問題 ************************************************/
45         List<String> tempPoliceNameStream = polices.stream().filter(p -> p.getPoliceAge() > 30)
46                 .sorted(Comparator.comparing(Police :: getPoliceAge))
47                 .map(Police :: getPoliceName)
48                 .collect(toList());
49         System.out.println(tempPoliceNameStream);
50         
51         //說明:若想並行執行,在Java8中,只須要將polices.stream() 改成polices.parallelStreamm()
52     }
53 }
54 
55 結果:
56 [張警官, 趙警官, 李警官, 周警官, 楊警官, 王警官, 劉警官]
57 -------------------------------- 分割線 ---------------------------------------
58 [張警官, 趙警官, 李警官, 周警官, 楊警官, 王警官, 劉警官]

    從以上一段代碼中,咱們能夠看出:多線程

  一、代碼是以聲明性方式編寫。即想要完成的工做,而非如何完成。app

  二、可使用操做鏈。filter以後的方法能夠直接點,直到完成。ide

    由上,咱們能夠先簡單的總結下使用流的好處:性能

  一、聲明性:更簡潔易讀。this

  二、可複用:更加靈活。spa

  三、可並行:性能更好。線程

2、流的介紹

    上面我看了流和集合的簡單比較,那麼到底流是什麼呢?咱們能夠簡單說明爲「從支持數據處理操做的源生成的元素序列」。咱們將這句話分開來解析:

  ①、元素序列:它就如何集合同樣,能夠訪問特定元素類型的一組有序值。但它與集合是不一樣的,集合是一種數據結構,它的主要目的是在必定時間和空間上存儲數據。而流主要用來計算。他們本質上是不一樣的。

  ②、源:即源頭,流在處理數據時,這個數據的源頭,例如:集合能夠是個源,文件也能夠是個源。

  ③、數據處理操做:流在處理數據時相似咱們操做數據庫,如:filter/map/sort等。流在處理數據時,可順序執行也可並行執行。

    流在操做中具備兩個很明顯的特徵:

  一、流水線。即流的操做返回的仍是一個流,如此多個操做就能夠一直日後連接,從而造成一個流水線。

  二、內部迭代。流在處理時,咱們是看不處處理過程的,它是在背後執行的。咱們能夠回看上一節中,民警在篩選/排序/映射到後面的截取/轉換等如何完成的,咱們沒法看到執行過程。

3、集合與流比對

    在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咱們沒法看到迭代細節。

4、流基本操做介紹

    從上一節,咱們能夠看到一段代碼

polices.stream().filter(p -> p.getPoliceAge() > 30) .sorted(Comparator.comparing(Police :: getPoliceAge)) .map(Police :: getPoliceName) .collect(toList());

    對於這段代碼咱們能夠將其分爲兩個部分或兩種操做:

  一、從stream方法以後,filter / sorted / map 返回的都爲一個流,組成一個流水線。

  二、collect方法執行後流即關閉,並返回一個非流對象。

    對於這兩個部分,咱們將第一種操做稱爲中間操做,第二種操做稱爲終端操做。

    在中間操做中,代碼的真正邏輯並無執行,只有當遇到了終端操做,以前的中間操做纔開始執行,知道結束並關閉流。

    由這些,咱們也能夠以總結下流的使用主要包含了三件事,即首先須要一個數據源用來執行查詢,再次須要一箇中間操做鏈造成一條流的流水線,最後須要一個終端操做來執行流水線並返回結果。

相關文章
相關標籤/搜索