到底什么是Stream流?


到底什么是Stream流?

什么是Stream?


\(~~~~~~~~\)我們先來看看Java里面是怎么定義Stream的:

A sequence of elements supporting sequential and parallel aggregate operations.
語翻譯過來就是:支持數據處理操作的一個source(資源?) 中的元素序列

\(~~~~~~~~\)讓我們解析一下上面的翻譯:

        List<Person> listPerson = new ArrayList<Person>() {
            {
                //元素序列-----翻譯過來就是集合中的所有元素對象
                add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 2000, 18));
                add(new Person("Tamsen", "Brittany", "Java programmer", "female", 2371, 55));
                add(new Person("Floyd", "Donny", "Java programmer", "male", 3322, 25));
                add(new Person("Sindy", "Jonie", "Java programmer", "female", 35020, 15));
                add(new Person("Vere", "Hervey", "Java programmer", "male", 2272, 25));
                add(new Person("Maude", "Jaimie", "Java programmer", "female", 2057, 87));
                add(new Person("Shawn", "Randall", "Java programmer", "male", 3120, 99));
                add(new Person("Jayden", "Corrina", "Java programmer", "female", 345, 25));
                add(new Person("Palmer", "Dene", "Java programmer", "male", 3375, 14));
                add(new Person("Addison", "Pam", "Java programmer", "female", 3426, 20));
            }
        };
        //獲取集合中年齡大於三十的人的名字,返回集合
        listPerson.stream().filter(e->e.getAge()>30).map(e->e.getFirstName()).collect(Collectors.toList());
  1. Sequence of elements(元素序列):簡單來說,就是我們操作的集合中的所有元素
  2. source(數據源) :Stream流的作用就是操作數據,那么source 就是為Stream提供可操作的源數據(一般,集合、數組或I/OI/O resources 都可以成為Stream的source )
  3. Data processing operations(數據處理操作):上面菜單程序代碼中出現的filter、sorted、map、collect,以及我們后來會用到的reduce、find、match`等都屬於Stream 的一些操作數據的方法接口。這些操作可以順序進行,也可以並行執行。
  4. Pipelining(管道、流水線):Stream對數據的操作類似數據庫查詢,也像電子廠的生產流線一樣,Stream的每一個中間操作(后面解釋什么是中間操作)比如上面的filter、sorted、map,每一步都會返回一個新的流,這些操作全部連起來就是想是一個工廠得生產流水線, like this:
  5. Internal iteration(內部迭代):Stream API 實現了對數據迭代的封裝,不用你再像操作集合一樣,手動寫for循環顯示迭代數據。

總結:Java8 中添加了一個新的接口類 Stream,相當於高級版的 Iterator,通過Lambda 表達式對集合進行各種非常便利、高效的聚合操作(Aggregate Operation),或者大批量數據操作 (Bulk Data Operation)Stream不僅可以通過串行的方式實現數據操作,還可以通過並行的方式處理大批量數據,提高數據的處理效率。

為什么使用Stream?


\(~~~~~~~~\)知道什么是Stream流,緊跟而來的就是我們為什么要使用Stream,換句話來說就是使用Stream有什么好處?
\(~~~~~~~~\)在《java8 in action》書中,作者說目前我們在幾乎所有開發中都會用到集合,但是目前集合在程序開發中的表現還不夠完美,比如你利用集合處理大量數據時,你不得不面對性能問題,不得不考慮進行並行代碼的編寫,這些工作都是比較繁重的,於是作者便創造了Stream 流。相比較Collection集合來說,Stream在開發中就具有許多獨特的優點,這些優點你可以先不用理解,知道就行,我們會在下面的案例代碼中直觀感受到:

  • 以聲明式的方式處理數據集合——更簡潔,更易讀
  • 可復合——更靈活
  • 可並行——無需寫任何多線程代碼,Stream API自動處理這些問題,性能更好

接下來我們來一個入門級的測試示例:

//Lists是Guava中的一個工具類
List<Integer> numberlist = Lists.newArrayList(1,null,3,4,null,6);

numberlist.stream().filter(num -> num != null).count();

根據我們的測試代碼,我們通過Stream獲取集合中不為null的元素個數,示例簡單只有一個操作。
根據測試示例詳細剖析Stream的通用語法:

    @Test
    public void test(){
        //Lists是Guava中的一個工具類
        List<Integer> numberlist = Lists.newArrayList(1,null,3,4,null,6);

        numberlist.stream().filter(num -> num != null).count();
        /**
         * 1.通過.Stream()創建Stream實例----------創建Stream
         * 2.通過.filter()中間操作,根據內部迭代對list集合中的每一個元素判斷是否為null---------轉換Stream
         * 3.通過。count()方法統計數量------------聚合
         */
    }

通過對代碼的分析,總結,Stream最主要的三組成部分:
\(~~~~~~~~\) 1. 創建流,也就是Stream開始的地方,負責創建一個Stream實例
\(~~~~~~~~\) 2. 中間操作,主要是一些對數據的過濾篩選,添加刪除等等操作,形成一個流程鏈。
\(~~~~~~~~\) 3. 收尾,也就是終端操作,我感覺更適合叫終結操作,終端操作會從流的流水線(中間操作)生成結果

Stream三大部分


\(~~~~~~~~\)通過對測試示例的解析,總結了stream的通用語法,總共包括三大部分,下面我們更加深入了解這三大部分:
\(~~~~~~~~\)還是文章開頭的示例:

        List<Person> listPerson = new ArrayList<Person>() {
            {
                //元素序列-----翻譯過來就是集合中的所有元素對象
                add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 2000, 18));
                add(new Person("Tamsen", "Brittany", "Java programmer", "female", 2371, 55));
                add(new Person("Floyd", "Donny", "Java programmer", "male", 3322, 25));
                add(new Person("Sindy", "Jonie", "Java programmer", "female", 35020, 15));
                add(new Person("Vere", "Hervey", "Java programmer", "male", 2272, 25));
                add(new Person("Maude", "Jaimie", "Java programmer", "female", 2057, 87));
                add(new Person("Shawn", "Randall", "Java programmer", "male", 3120, 99));
                add(new Person("Jayden", "Corrina", "Java programmer", "female", 345, 25));
                add(new Person("Palmer", "Dene", "Java programmer", "male", 3375, 14));
                add(new Person("Addison", "Pam", "Java programmer", "female", 3426, 20));
            }
        };
        //獲取集合中年齡大於三十的人的名字,返回集合
        listPerson
                .stream()//創建Stream實例
                .filter(e->e.getAge()>30).map(e->e.getFirstName())//中間操作
                .collect(Collectors.toList());//終端操作

創建Stream實例


通過集合、數組等都可以創建Stream流實例,博客上有做詳細的講解【Stream初步認識(一)】,這里就不詳細說了。

中間操作


可以連接起來的Stream流操作,諸如filter或sorted等可以返回一個Stream 流的操作,就叫中間操作。只有存在一個終端操作,且觸發了終端操作,中間操作才會開始執行。
在上面的測試方法中,創建完實例之后我們,連續調用filter()以及map()方法,我們先看一下他們的源碼:

    //filter源碼
    Stream<T> filter(Predicate<? super T> predicate);
    
    //map源碼
  <R> Stream<R> map(Function<? super T, ? extends R> mapper);

調用filter()以及map()方法,都會返回stream流,這也是形成鏈式調用的主要原因。
(雖然filter和map是兩個獨立的中間操作,但它們合並到同一次遍歷中執行,這就叫作循環合並。)

終端操作


可以啟動中間操作和關閉中間流的操作稱為終端操作,終端操作會從流的流水線(中間操作)生成結果。其結果是任何非Stream的值,比如可能是List、Integer,甚至void。
而在我們的測試示例中,我們返回的就是集合

 //獲取集合中年齡大於三十的人的名字,返回集合
        List<String> reList = listPerson
                .stream()//創建Stream實例
                .filter(e -> e.getAge() > 30).map(e -> e.getFirstName())//中間操作
                .collect(Collectors.toList());//終端操作

Stream流的生命周期


\(~~~~~~~~\)同一個流只能遍歷一次,遍歷完后,這個流就已經被消費掉了。你如果還需要在遍歷,可以從原始數據源那里再獲得一個新的流來重新遍歷一遍。
比如:

    List<Person> listPerson = new ArrayList<Person>() {
            {
                //元素序列-----翻譯過來就是集合中的所有元素對象
                add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 2000, 18));
                add(new Person("Tamsen", "Brittany", "Java programmer", "female", 2371, 55));
                add(new Person("Floyd", "Donny", "Java programmer", "male", 3322, 25));
                add(new Person("Sindy", "Jonie", "Java programmer", "female", 35020, 15));
                add(new Person("Vere", "Hervey", "Java programmer", "male", 2272, 25));
                add(new Person("Maude", "Jaimie", "Java programmer", "female", 2057, 87));
                add(new Person("Shawn", "Randall", "Java programmer", "male", 3120, 99));
                add(new Person("Jayden", "Corrina", "Java programmer", "female", 345, 25));
                add(new Person("Palmer", "Dene", "Java programmer", "male", 3375, 14));
                add(new Person("Addison", "Pam", "Java programmer", "female", 3426, 20));
            }
        };
        //獲取集合中年齡大於三十的人的名字,返回集合
        List<String> reList = listPerson
                .stream()//創建Stream實例
                .filter(e -> e.getAge() > 30).map(e -> e.getFirstName())//中間操作
                .collect(Collectors.toList());//終端操作
        Stream<Person> stream1 = listPerson.stream();
        stream1.forEach(System.out::print);
        stream1.forEach(System.out::print);

打印結果:

同一個流 s 被兩次用於forEach的終端操作,此時控制台報錯,提示Stream流已被操作或者關閉。
但是當我們從原始數據源集合中那里再獲得一個新的流在操作就可以:

        //獲取集合中年齡大於三十的人的名字,返回集合
        List<String> reList = listPerson
                .stream()//創建Stream實例
                .filter(e -> e.getAge() > 30).map(e -> e.getFirstName())//中間操作
                .collect(Collectors.toList());//終端操作
        Stream<Person> stream1 = listPerson.stream();
        stream1.forEach(System.out::print);
        
        Stream<Person> stream2 = listPerson.stream();
        stream2.forEach(System.out::print);

小結

1、流的使用一般包括三件事:

  • 一個數據源(如集合)來執行一個查詢;
  • 一個中間操作鏈,形成一條流的流水線;
  • 一個終端操作,執行流水線,並能生成結果。
    2、Stream API提供的一些常用操作接口:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM