1. 程式人生 > >jdk1.8特性——lambda表示式、stream學習,結合使用

jdk1.8特性——lambda表示式、stream學習,結合使用

       最近再專案中用到了lambda和Stream,發現用起來程式碼很簡潔,就是有些複雜點的可能用完後可讀性不是很理想,但是簡單點的還是很好理解的,因此專門試了試,感覺真的很棒~先來了解一下

一:lambda表示式

        lambda語法:

    1.多引數

     1. lambda表示式的基本格式為(x1,x2)->{表示式...};

     2. 在上式中,lambda表示式帶有兩個引數,此時引數型別可以省略,但兩邊的括號不能省略

     3. 如果表示式只有一行,那麼表示式兩邊的花括號可以省略

public static  void test1_() {
    List<User> usersList = new ArrayList<User>() {
        {
            add(new User("user1", "stu1", 10));
            add(new User("user2", "stu2", 6));
            add(new User("user3", "stu3", 8));
            add(new User("user4", "stu4", 7));
        }
    };
    Collections.sort(usersList, (s1, s2) -> Integer.compare(s1.getAge(), s2.getAge()));
    System.out.println(usersList);
}

    2.對於沒有引數的情況

      1.引數的括號不能省略,

      2.其他語法同多引數

如:無lambda

public static void testThread(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("hello, testThread !");
        }
    }).start();
}

如:通過lambda寫法

public static void testThreadLambda(){
    new Thread(()-> System.out.println("hello, testThreadLambda!")).start();
}

一:stream()

  1. Stream是元素的集合,這點讓Stream看起來用些類似Iterator
  2. 可以支援順序和並行的對原Stream進行匯聚的操作;

        可以認為Stream是一個高階版本的iterator,原始版本的iterator,使用者只能一個一個的遍歷元素並對其執行某些操作,而stream,只要給出需要對其包含的元素執行什麼操作,比如“獲取每個字串的首字母”,“將每個字串的字母改為大寫”等,具體的這些操作如何應用到每個元素上,交給stream就好了。

List<Integer> nums = new ArrayList<Integer>(){
     {
         add(1);
         add(null);
         add(2);
         add(3);
         add(null);
     }
 };
Long numCount = nums.stream().filter(num -> num != null).count();
System.out.println("numCount="+numCount);

 借用別人的圖來分析一下~哈哈

                      

        可以看到紅色區域是Strean的生命開始的地方,負責建立一個Stream例項;綠色框中的語句是賦予Stream靈魂的地方,把一個Stream轉換成另一個Stream,紅框中的語句生成的是包含所有nums變數的Stream,經過綠框的filter以後,重新生成了一個過濾掉原nums列表所有null以後的Stream, 藍色框中的語句是把Stream的裡面包含的內容按照某種演算法來匯聚成一個值,例子中是獲取Stream中包含的元素個數。

總結一下使用Stream的基本步驟:

  1. 建立Stream。
  2. 轉換Stream,每次轉換原有Stream物件不改變,返回的是一個新的Stream物件(可以有多次轉換)。
  3. 對Stream進行聚合操作,獲取想要的結果,也可進行集合轉換,得到想要的集合結果。

建立Stream

       建立Stream,常用的兩種方式,1通過Stream介面的靜態工廠方法;2通過Collection介面的預設方法–stream(),把一個Collection物件轉換成Stream。

轉化Stream

        轉換Stream其實就是把一個Stream通過某些行為轉換成一個新的Stream。Stream介面中定義了幾個常用的轉換方法:

       1. distinct: 對於Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重複的元素;

       2. filter: 對於Stream中包含的元素使用給定的過濾函式進行過濾操作,新生成的Stream只包含符合條件的元素;

       3.map: 對於Stream中包含的元素使用給定的轉換函式進行轉換操作,新生成的Stream只包含轉換生成的元素。這個方法有三個對於原始型別的變種方法,分別是:mapToInt,mapToLong和mapToDouble。這三個方法也比較好理解,比如mapToInt就是把原始Stream轉換成一個新的Stream,這個新生成的Stream中的元素都是int型別。之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗;

      4.flatMap:和map類似,不同的是其每個元素轉換得到的是Stream物件,會把子Stream中的元素壓縮到父集合中;

      5.peek: 生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函式(Consumer例項),新Stream每個元素被消費的時候都會執行給定的消費函式;

      6. limit: 對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小於N,那就獲取其所有的元素;

      7. skip: 返回一個丟棄原Stream的前N個元素後剩下元素組成的新Stream,如果原Stream中包含的元素個數小於N,那麼返回空Stream;

走一波試試:

List<Integer> nums = new ArrayList<Integer>(){
    {
        add(1);
        add(null);
        add(2);
        add(3);
        add(null);
        add(5);
        add(3);
        add(3);
        add(null);
        add(5);
    }
};
System.out.println("sum is:"+nums.stream().filter(num -> num != null).distinct().mapToInt(num -> num *2).peek(System.out::println).skip(2).limit(4).sum());

        這波先將nums進行非空過濾,留下所有非空元素,然後對於留下非空元素進行去重,然後再每個元素乘以2,再每個元素被消費的時候列印自身,在跳過前兩個元素,最後去前四個元素進行加和運算。當然啦,前四個元素不夠就有幾個算幾個啦。

效能問題:

        有些細心的同學可能會有這樣的疑問:在對於一個Stream進行多次轉換操作,每次都對Stream的每個元素進行轉換,而且是執行多次,這樣時間複雜度就是一個for迴圈裡把所有操作都做掉的N(轉換的次數)倍啊。其實不是這樣的,轉換操作都是lazy的,多個轉換操作只會在匯聚操作(見下節)的時候融合起來,一次迴圈完成。我們可以這樣簡單的理解,Stream裡有個操作函式的集合,每次轉換操作就是把轉換函式放入這個集合中,在匯聚操作的時候迴圈Stream對應的集合,然後對每個元素執行所有的函式。

        Stream介面有一些通用的匯聚操作,比如reduce()和collect();也有一些特定用途的匯聚操作,比如sum(),max()和count()。注意:sum方法不是所有的Stream物件都有的,只有IntStream、LongStream和DoubleStream是例項才有。