java8新特性——Stream API


  Java8中有兩大最為重要得改變,其一時Lambda表達式,另外就是 Stream API了。在前面幾篇中簡單學習了Lambda表達式得語法,以及函數式接口。本文就來簡單學習一下Stream API(java.util.stream.*)。

  Stream 是 Java8中處理集合得關鍵抽象概念,他可以指定你希望對集合進行得操作,可以執行非常復雜得查找、過濾和映射數據等操作。使用Stream API對集合數據進行操作,就類似使用SQL執行得數據庫查詢。也可以使用S他ream API 來並行執行操作。簡而言之,Stream API 提供了一種高效且易於使用得處理數據得方式。

  在Stream操作過程中,可以對數據流做過濾,排序,切片等操作,但是操作之后會產生一個新的流,而數據源則不會發生改變。

 

一、什么是 Stream

  Stream是數據渠道,用於操作數據源(集合,數組等)所生成得元素序列。而集合講得是數據,流講得是計算。

  注意:

    ①. Stream 自己不會存儲元素。

    ②. Stream 不會改變源對象。相反,它會返回一個持有結果得新Stream

    ③. Stream 操作時延遲執行得,這意味着它們會等到需要結果時才執行。(延遲加載)

 

二、Stream 操作的三個步驟

  1). 創建 Stream

    一個數據源(集合,數組),獲取一個流。

  2). 中間操作

    一個中間操作鏈,對數據源的數據進行處理。

  3). 終止操作

    一個終止操作,執行中間操作鏈,並產生結果。

 

三、創建Stream 的四種方式

  1). 通過Collection得Stream()方法(串行流)或者 parallelStream()方法(並行流)創建Stream。

 1   /**
 2      * 創建 Stream的四種方式
 3      * 1.通過Collection得Stream()方法(串行流)
 4             或者 parallelStream()方法(並行流)創建Stream
 5      */
 6     @Test
 7     public void test1 () {
 8         
 9         //1. 通過Collection得Stream()方法(串行流)
10         //或者 parallelStream()方法(並行流)創建Stream
11         List<String> list = new ArrayList<String>();
12         Stream<String> stream1 = list.stream();
13         
14         Stream<String> stream2 = list.parallelStream();
15         
16     }

 

  2).通過Arrays中得靜態方法stream()獲取數組流

 1     /**
 2      * 創建 Stream的四種方式
 3      * 2. 通過Arrays中得靜態方法stream()獲取數組流
 4      */
 5     @Test
 6     public void test2 () {
 7         
 8         //2. 通過Arrays中得靜態方法stream()獲取數組流
 9         IntStream stream = Arrays.stream(new int[]{3,5});
10         
11     }

 

  3). 通過Stream類中得 of()靜態方法獲取流

 1     /**
 2      * 創建 Stream的四種方式
 3      * 3. 通過Stream類中得 of()靜態方法獲取流
 4      */
 5     @Test
 6     public void test3 () {
 7         
 8         //3. 通過Stream類中得 of()靜態方法獲取流
 9         Stream<String> stream = Stream.of("4645", "huinnj");
10         
11     }

 

  4). 創建無限流(迭代、生成)

 1 /**
 2      * 創建 Stream的四種方式
 3      * 4. 創建無限流(迭代、生成)
 4      */
 5     @Test
 6     public void test4 () {
 7         
 8         //4. 創建無限流
 9         //迭代(需要傳入一個種子,也就是起始值,然后傳入一個一元操作)
10         Stream<Integer> stream1 = Stream.iterate(2, (x) -> x * 2);
11         
12         //生成(無限產生對象)
13         Stream<Double> stream2 = Stream.generate(() -> Math.random());
14         
15     }

 

四、Stream 中間操作

  多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何得處理!而終止操作時一次性全部處理,稱為‘延遲加載’

  1). 篩選與切片

    ①. filter —— 接收Lambda ,從流中排除某些元素。

 1     /**
 2      * 篩選與切片
 3      *  filter —— 接收Lambda ,從流中排除某些元素。
 4      *  
 5      */
 6     @Test
 7     public void test5 () {
 8         //內部迭代:在此過程中沒有進行過迭代,由Stream api進行迭代
 9         //中間操作:不會執行任何操作
10         Stream<Person> stream = list.stream().filter((e) -> {
11             System.out.println("Stream API 中間操作");
12             return e.getAge() > 30;
13         });
14         
15         //終止操作:只有執行終止操作才會執行全部。即:延遲加載 
16         stream.forEach(System.out :: println);
17     
18     }

  執行上面方法,得到下面結果。

Person [name=張三, sex=男, age=76]
Stream API 中間操作
Stream API 中間操作
Person [name=王五, sex=男, age=35]
Stream API 中間操作
Stream API 中間操作
Person [name=錢七, sex=男, age=56]
Stream API 中間操作
Person [name=翠花, sex=女, age=34]

  我們,在執行終止語句之后,一邊迭代,一邊打印,而我們並沒有去迭代上面集合,其實這是內部迭代,由Stream API 完成。

  下面我們來看看外部迭代,也就是我們人為得迭代。

1   @Test
2     public void test6 () {
3         //外部迭代
4         Iterator<Person> it = list.iterator();
5         while (it.hasNext()) {
6             System.out.println(it.next());
7         }
8     
9     }

 

    ②. limit —— 截斷流,使其元素不超過給定數量。

 

 1     /**
 2      * limit —— 截斷流,使其元素不超過給定數量。
 3      */
 4     @Test
 5     public void test7 () {
 6         //過濾之后取2個值
 7         list.stream().filter((e) -> e.getAge() >30 ).
 8         limit(2).forEach(System.out :: println);
 9     
10     }

  在這里,我們可以配合其他得中間操作,並截斷流,使我們可以取得相應個數得元素。而且在上面計算中,只要發現有2條符合條件得元素,則不會繼續往下迭代數據,可以提高效率。

 

  2). 跳過元素

    skip(n),返回一個扔掉了前n個元素的流。若流中元素不足n個,則返回一個空,與limit(n)互補。

 1     /**
 2      * skip(n)—— 跳過元素,返回一個扔掉了前n個元素的流。
 3      * 若流中元素不足n個,則返回一個空,與limit(n)互補。
 4      */
 5     @Test
 6     public void test8 () {
 7         //跳過前2個值
 8         list.stream().skip(2).forEach(System.out :: println);
 9     
10     }
11      

 

   3).  篩選

    distinct 通過流所生成元素的hashCode()和equals()去除重復元素

 

1     /**
2      * distinct —— 篩選,通過流所生成元素的hashCode()和equals()去除重復元素
3      */
4     @Test
5     public void test9 () {
6         
7         list.stream().distinct().forEach(System.out :: println);
8     
9     }

 

  注意:distinct 需要實體中重寫hashCode()和 equals()方法才可以使用

 

  4). 映射

    ① . map ,將元素轉換成其他形式或者提取信息。接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素。

 

 1     /**
 2      * map —— 映射 ,將元素轉換成其他形式或者提取信息。接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素。
 3      */
 4     @Test
 5     public void test10 () {
 6         //將流中每一個元素都映射到map的函數中,每個元素執行這個函數,再返回
 7         List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
 8         list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
 9     
10         //獲取Person中的每一個人得名字name,再返回一個集合
11         List<String> names = this.list.stream().map(Person :: getName).
12             collect(Collectors.toList());
13     }

 

 

    ② . flatMap —— 接收一個函數作為參數,將流中的每個值都換成一個流,然后把所有流連接成一個流

 

 

 1     /**
 2      * flatMap —— 接收一個函數作為參數,將流中的每個值都換成一個流,然后把所有流連接成一個流
 3      */
 4     @Test
 5     public void test11 () {
 6         StreamAPI_Test s = new StreamAPI_Test();
 7         List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
 8         list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
 9         
10         //如果使用map則需要這樣寫
11         list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
12             e.forEach(System.out::println);
13         });
14     }
15     
16     /**
17      * 將一個字符串轉換為流
18      * @param str
19      * @return
20      */
21     public Stream<Character> filterCharacter(String str){
22         List<Character> list = new ArrayList<>();
23         for (Character ch : str.toCharArray()) {
24             list.add(ch);
25         }
26         return list.stream();
27     }

 

  其實map方法就相當於Collaction的add方法,如果add的是個集合得話就會變成二維數組,而flatMap 的話就相當於Collaction的addAll方法,參數如果是集合得話,只是將2個集合合並,而不是變成二維數組。

 

  5). 排序

     sorted有兩種方法,一種是不傳任何參數,叫自然排序,還有一種需要傳Comparator 接口參數,叫做定制排序。

 

 

 1     /**
 2      * sorted有兩種方法,一種是不傳任何參數,叫自然排序,還有一種需要傳Comparator 接口參數,叫做定制排序。
 3      */
 4     @Test
 5     public void test12 () {
 6         // 自然排序
 7         List<Person> persons = list.stream().sorted().collect(Collectors.toList());
 8         
 9         //定制排序
10         List<Person> persons1 = list.stream().sorted((e1, e2) -> {
11             if (e1.getAge() == e2.getAge()) {
12                 return 0;
13             } else if (e1.getAge() > e2.getAge()) {
14                 return 1;
15             } else {
16                 return -1;
17             }
18         }).collect(Collectors.toList());
19     }

 

 

 

五、Stream 終止操作

  1). 查找與匹配

    首先我們先創建一個集合。

 

 1         List<Person> persons = Arrays.asList(
 2             new Person("張三", "男", 76, Status.FREE),
 3             new Person("李四", "女", 12, Status.BUSY),
 4             new Person("王五", "男", 35, Status.BUSY),
 5             new Person("趙六", "男", 3, Status.FREE),
 6             new Person("錢七", "男", 56, Status.BUSY),
 7             new Person("翠花", "女", 34, Status.VOCATION),
 8             new Person("翠花", "女", 34, Status.FREE),
 9             new Person("翠花", "女", 34, Status.VOCATION)
10             );

 

    ①. allMatch —— 檢查是否匹配所有元素。

 

1     /**
2      * allMatch —— 檢查是否匹配所有元素。
3      * 判斷所有狀態是否都是FREE
4      */
5     @Test
6     public void test13 () {
7          boolean b = persons.stream().allMatch((e) -> Status.FREE.equals(e.getStatus()));
8          System.out.println(b);
9     }

 

 

 

    ②. anyMatch —— 檢查是否至少匹配所有元素。

 

1     /**
2      * anyMatch —— 檢查是否至少匹配所有元素。
3      * 判斷是否有一個是FREE
4      */
5     @Test
6     public void test14 () {
7          boolean b = persons.stream().anyMatch((e) -> Status.FREE.equals(e.getStatus()));
8          System.out.println(b);
9     }

 

 

 

    ③. noneMatch —— 檢查是否沒有匹配所有元素。

 

1     /**
2      * noneMatch —— 檢查是否沒有匹配所有元素。
3      * 判斷是否沒有FREE
4      */
5     @Test
6     public void test15 () {
7          boolean b = persons.stream().noneMatch((e) -> Status.FREE.equals(e.getStatus()));
8          System.out.println(b);
9     }

 

 

 

    ④. findFirst —— 返回第一個元素。

 

 1     /**
 2      * findFirst —— 返回第一個元素。
 3      *
 4      */
 5     @Test
 6     public void test16 () {
 7          Optional<Person> person = persons.stream().findFirst();
 8          System.out.println(person);
 9          
10          person.orElse(new Person("王五", "男", 35, Status.BUSY));
11     }

 

  注意:上面findFirst 返回的是一個Optional的對像,他將我們的Person封裝了一層,這是為了避免空指針。而且這個對象為我們提供了一個orElse方法,就是當我們得到的這個對象為空時,我們可以傳入一個新得對象去替代它。

 

 

    ⑤. findAny —— 返回當前流中任意元素。

 

 1     /**
 2      * findAny —— 返回當前流中任意元素。
 3      */
 4     @Test
 5     public void test17 () {
 6          Optional<Person> person = persons.stream().findAny();
 7          System.out.println(person);
 8          
 9          person.orElse(new Person("王五", "男", 35, Status.BUSY));
10     }

 

 

 

    ⑥. count —— 返回流中元素總個數。

 

1     /**
2      * count —— 返回流中元素總個數。
3      */
4     @Test
5     public void test18 () {
6          long count = persons.stream().count();
7          System.out.println(count);
8          
9     }

 

 

 

    ⑦. max —— 返回流中最大值。

 

1     /**
2      * max —— 返回流中最大值。
3      */
4     @Test
5     public void test18 () {
6         Optional<Person> person = persons.stream().max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
7          System.out.println(person);
8          
9     }

 

 

 

    ⑧. min —— 返回流中最小值。

 

1     /**
2      * min —— 返回流中最小值。
3      */
4     @Test
5     public void test20 () {
6         Optional<Person> person = persons.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
7          System.out.println(person);
8          
9     }

 

  2). 歸約(可以將流中元素反復結合在一起,得到一個值)

    ①. reduce(T identitty,BinaryOperator)首先,需要傳一個起始值,然后,傳入的是一個二元運算。

1     /**
2      * reduce(T identitty,BinaryOperator)首先,需要傳一個起始值,然后,傳入的是一個二元運算。
3      */
4     @Test
5     public void test21 () {
6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7         Integer sum = list.stream().reduce(0, (x, y) -> x + y);
8     }

 

    ②. reduce(BinaryOperator)此方法相對於上面方法來說,沒有起始值,則有可能結果為空,所以返回的值會被封裝到Optional中。

1     /**
2      *  reduce(BinaryOperator)此方法相對於上面方法來說,沒有起始值,則有可能結果為空,所以返回的值會被封裝到Optional中
3      */
4     @Test
5     public void test22 () {
6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7         Optional<Integer> sum = list.stream().reduce(Integer :: sum);
8     }

 

  備注:map和reduce的連接通常稱為map-reduce模式,因Google用它來進行網絡搜索而出名。

 

  3). 收集collect(將流轉換為其他形式。接收一個Collector接口得實現,用於給其他Stream中元素做匯總的方法)

    Collector接口中方法得實現決定了如何對流執行收集操作(如收集到List,Set,Map)。但是Collectors實用類提供了很多靜態方法,可以方便地創建常見得收集器實例。

    ①. Collectors.toList() 將流轉換成List

1     /**
2      *  Collectors.toList() 將流轉換成List
3      */
4     @Test
5     public void test23() {
6         7         List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
8     }

 

    ②. Collectors.toSet()將流轉換為Set

1     /**
2      *  Collectors.toSet() 將流轉換成Set
3      */
4     @Test
5     public void test24() {
6         7         Set<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toSet());
8     }

 

    ③. Collectors.toCollection()將流轉換為其他類型的集合

1     /**
2      *  Collectors.toCollection()將流轉換為其他類型的集合
3      */
4     @Test
5     public void test25() {
6         7         LinkedList<String> names =  this.list.stream().map(Person :: getName).collect(Collectors.toCollection(LinkedList :: new));
8     }

    

    ④. Collectors.counting()  元素個數

1     /**
2      *  Collectors.counting()  總數
3      */
4     @Test
5     public void test26() {
6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7         Long count =  list.stream().collect(Collectors.counting());
8     }

 

    ⑤. Collectors.averagingDouble()、Collectors.averagingDouble()、Collectors.averagingLong() 平均數,這三個方法都可以求平均數,不同之處在於傳入得參數類型不同,返回值都為Double

 1     /**
 2      *  Collectors.averagingInt() 、
 3      *   Collectors.averagingDouble()、
 4      *    Collectors.averagingLong() 平均數,
 5      *    者三個方法都可以求平均數,不同之處在於傳入得參數類型不同,
 6      *    返回值都為Double
 7      */
 8     @Test
 9     public void test27() {
10         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
11         Double avg =  list.stream().collect(Collectors.averagingInt((x) -> x));
12     }

 

    ⑥. Collectors.summingDouble()、Collectors.summingDouble()、Collectors.summingLong() 求和,不同之處在於傳入得參數類型不同,返回值為Integer, Double, Long

 1     /**
 2      *  Collectors.summingInt() 、
 3      *   Collectors.summingDouble()、
 4      *    Collectors.summingLong() 求和,
 5      *    者三個方法都可以求總數,不同之處在於傳入得參數類型不同,
 6      *    返回值為Integer, Double, Long
 7      */
 8     @Test
 9     public void test28() {
10         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
11         Integer sum =  list.stream().collect(Collectors.summingInt((x) -> x));
12     }

 

  ⑦. Collectors.maxBy() 求最大值

1     /**
2      * Collectors.maxBy() 求最大值
3      */
4     @Test
5     public void test29() {
6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
7         Optional<Integer> max =  list.stream().collect(Collectors.maxBy((x, y) ->Integer.compare(x, y)));
8     }

 

  ⑧. Collectors.minBy() 求最小值

/**
     * Collectors.minBy() 求最小值
     */
    @Test
    public void test29() {
        List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Optional<Integer> min =  list.stream().collect(Collectors.minBy((x, y) ->Integer.compare(x, y)));
    }

 

  ⑨. Collectors.groupingBy()分組 ,返回一個map

1     /**
2      * Collectors.groupingBy()分組 ,返回一個map
3      */
4     @Test
5     public void test30() {
6         Map<String, List<Person>> personMap =  list.stream().collect(Collectors.groupingBy(Person :: getSex));
7     }

      Collectors.groupingBy()還可以實現多級分組

1     /**
2      * Collectors.groupingBy()多級分組 ,返回一個map
3      */
4     @Test
5     public void test31() {
6         Map<String, Map<Status, List<Person>>> personMap =  list.stream().collect(Collectors.groupingBy(Person :: getSex, Collectors.groupingBy(Person :: getStatus)));
7     }

 

  ⑩. Collectors.partitioningBy() 分區,參數中傳一個函數,返回true,和false 分成兩個區

1     /**
2      * Collectors.partitioningBy() 分區,參數中傳一個函數,返回true,和false 分成兩個區
3      */
4     @Test
5     public void test32() {
6         Map<Boolean, List<Person>> personMap =  list.stream().collect(Collectors.partitioningBy((x) -> x.getAge() > 30));
7     }

 

  上面就是Stream的一些基本操作,只要勤加練習就可以靈活使用,而且效率大大提高。

 


免責聲明!

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



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