JDK 1.8 新特性學習(Stream)

mike曉發表於2019-01-19

Stream實現了對資料來源的流式處理,它可以並行操作,提高資料處理效率。

什麼是流

流不是集合,它不對資料做儲存,只是最資料進行演算法處理,比如最大值,最小值,排序等操作。Stream會在資料來源內部隱式的遍歷進行處理。Stream會並行遍歷資料,將資料分成若干段,同時進行處理,最終彙總結果一起輸出。

Stream 就如同一個迭代器(Iterator),單向,不可往復,資料只能遍歷一次,遍歷過一次後即用盡了,就好比流水從面前流過,一去不復返。

特點

首先對stream的操作可以分為兩類,中間操作(intermediate operations)和結束操作(terminal operations):

  • 中間操作總是會惰式執行,呼叫中間操作只會生成一個標記了該操作的新stream。
  • 結束操作會觸發實際計算,計算髮生時會把所有中間操作積攢的操作以pipeline的方式執行,這樣可以減少迭代次數。計算完成之後stream就會失效。
  • 無儲存。stream不是一種資料結構,它只是某種資料來源的一個檢視,資料來源可以是一個陣列,Java容器或I/O channel等。
  • 為函數語言程式設計而生。對stream的任何修改都不會修改背後的資料來源,比如對stream執行過濾操作並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream。
  • 惰式執行。stream上的操作並不會立即執行,只有等到使用者真正需要結果的時候才會執行。
  • 可消費性。stream只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。

使用方法

1.構造流的方法

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        //1. of
        Stream<String> stream  = Stream.of("hello","java","python");
        // 2. Arrays
        String [] strArray = new String[] {"hello","java","python"};
        stream = Stream.of(strArray);
        stream = Arrays.stream(strArray);
        // 3. Collections
        List<String> list = Arrays.asList(strArray);
        stream = list.stream();
        System.out.println(stream.findAny());
    }

}

最終只返回第一個結果: Optional[hello]

2. 流轉換為其它資料結構

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python"};
        List<String> list = Arrays.asList(strArray);
        // to array
        System.out.println(list.stream().toArray()[0]);
        // to list
        System.out.println(list.stream().collect(Collectors.toList()));
        // to string
        System.out.println(list.stream().collect(Collectors.joining()).toString());
    }

}

輸出:
hello
[hello, java, python]
hellojavapython

3. 流的操作

  • Intermediate (map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered)
  • Terminal(forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator)
  • Short-circuiting(anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit)

一個流可以後面跟隨零個或多個 intermediate 操作。其目的主要是開啟流,做出某種程度的資料對映/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅呼叫到這類方法,並沒有真正開始流的遍歷。

一個流只能有一個 terminal 操作,當這個操作執行後,流就被使用“光”了,無法再被操作。所以這必定是流的最後一個操作。Terminal 操作的執行,才會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。

4.基礎的使用

1.map+forEach

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python"};
        List<String> list = Arrays.asList(strArray);
        list.stream().map((v) ->v.toUpperCase())
        .forEach(t -> System.out.println(t));
    }

}

將list中的所有字母轉換成大寫,然後遍歷輸出。 實際list中的值並沒有改變,我們只是藉助Stream來做業務處理。
輸出 :
HELLO
JAVA
PYTHON

2.filter+map+forEach

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python"};
        List<String> list = Arrays.asList(strArray);
        list.stream().filter(f -> f.length()>4)
        .map(v ->v.toUpperCase())
        .forEach(t -> System.out.println(t));
    }

}

先filter過濾,然後map字母大寫,最後forEach輸出結果:
HELLO
PYTHON

3. filter+sorted+map+forEach

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python","node","react","vue"};
        List<String> list = Arrays.asList(strArray);
        list.stream().filter(f -> f.length()>3)
        .sorted((a,b) -> b.compareTo(a))
        .map(v ->v.toUpperCase())
        .forEach(t -> System.out.println(t));
    }

}

先filter過濾,然後sorted排序,然後map字母大寫,最後forEach輸出結果:
REACT
PYTHON
NODE
JAVA
HELLO

4. filter+sorted+map+distinct+forEach

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python","node","react","vue","React"};
        List<String> list = Arrays.asList(strArray);
        list.stream().filter(f -> f.length()>3)
        .sorted((a,b) -> b.compareTo(a))
        .map(v ->v.toUpperCase())
        .distinct()
        .forEach(t -> System.out.println(t));
    }

}

distinct去重,使用 Object.equals(Object)來判斷是否重複,最終只留下一個REACT,結果:
REACT
PYTHON
NODE
JAVA
HELLO

5. filter+sorted+map+distinct+limit+forEach

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python","node","react","vue","React"};
        List<String> list = Arrays.asList(strArray);
        list.stream().filter(f -> f.length()>3)
        .sorted((a,b) -> b.compareTo(a))
        .map(v ->v.toUpperCase())
        .distinct()
        .limit(3)
        .forEach(t -> System.out.println(t));
    }

}

limit限制元素個數,這裡著要前3個結果:
REACT
PYTHON
NODE

6.filter+sorted+map+distinct+limit+peek+forEach

public class StreamStudy {
    
    public static void main(String[] args) throws Exception {
        String [] strArray = new String[] {"hello","java","python","node","react","vue","React"};
        List<String> list = Arrays.asList(strArray);
        list.stream().filter(f -> f.length()>3)
        .sorted((a,b) -> b.compareTo(a))
        .map(v ->v.toUpperCase())
        .distinct()
        .limit(3)
        .peek(p -> p.toLowerCase())
        .forEach(t -> System.out.println(t));
        
    }

}

peek會對每個元素執行操作,並返回包含原stream元素的新Stream,什麼意思呢?先看結果:
REACT
PYTHON
NODE
並不是我們看到的小寫,因為peek產生的新的stream並不是我們已開始處理的Stream,所以我們看到的還是大寫。如果你的處理過程中涉及一些額外邏輯,但不影響最終結果,那麼你可以使用peek去搞一個新的Stream去處理。

7.總結

我們主要使用的是Intermediate 中的方法進行資料處理,Terminal 中的方法只能使用一個,這意味著對流的處理終止,這時才開始執行前面的那些Intermediate方法。最後對一些方法作一些解釋,就不一一演示了:
forEach遍歷、 forEachOrdered按順序遍歷、 toArray結果轉換成陣列、 reduce結果中的元素進行組合、 collect結果轉換成集合、 min結果中最小值、 max結果中最大值、 count結果中元素數量、 anyMatch結果中存在元素滿足某一條件、 allMatch結果中所有元素都滿足某一條件、 noneMatch結果中所有元素都不滿足某一條件、 findFirst結果中第一條資料 、 findAny結果中的任意一條資料、 iterator遍歷

歡迎關注我的公眾號mike啥都想搞,有更多教程資料相送。

相關文章