Lambda表達式,Stream流

Lambda

前言

    當我們想要使用一個線程,我們其實並不想真的創建一個匿名內部類對象。我們只是爲了做這件事情而不得不創建一個對象。我們真正希望做的事情是:將 run 方法體內的代碼傳遞給 Thread 類。

傳統的使用一個線程

public class Test {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多線程任務執行!");
            }
        }).start();
    }
}

對於 Runnable 的匿名內部類用法,可以分析出幾點內容:

  • Thread 類需要 Runnable 接口作爲參數,其中的抽象 run 方法是用來指定線程任務內容的核心;
  • 爲了指定 run 的方法體,不得不需要 Runnable 接口的實現類;
  • 爲了省去定義一個 RunnableImpl 實現類的麻煩,不得不使用匿名內部類;
  • 必須覆蓋重寫抽象 run 方法,所以方法名稱、方法參數、方法返回值不得不再寫一遍,且不能寫錯;
  • 而實際上,似乎只有方法體纔是關鍵所在

使用Lambda表達式寫法

public class Test {
    public static void main(String[] args) {
        new Thread(()->System.out.println("多線程任務執行!")).start();
    }
}

這段代碼和上面的傳統寫法功能一樣,但卻是肉眼可見的簡單!!

使用Lambda表達式的前提

那程序在滿足什麼條件的時候纔可以使用Lambda表達式呢??

  1. 使用Lambda必須具有接口,且要求接口中有且僅有一個抽象方法。 無論是JDK內置的 Runnable 、Comparator 接口還是自定義的接口,只有當接口中的抽象方法存在且唯一時,纔可以使用Lambda。
  2. 使用Lambda必須具有上下文推斷。 也就是方法的參數或局部變量類型必須爲Lambda對應的接口類型,才能使用Lambda作爲該接口的實例。

備註:有且僅有一個抽象方法的接口,稱爲“函數式接口”。

Lambda表達式格式

    一個參數 一個箭頭 一段代碼

(參數類型 參數名)->{代碼語句}//如果只有一條語句{}大括號也可以省略

格式說明:

  • 小括號內的語法與傳統方法參數列表一致:無參數則留空;多個參數則用逗號分隔
  • 中間的一個箭頭代表將前面的參數傳遞給後面的代碼;
  • 大括號內的語法與傳統方法體要求基本一致。

Lambda表達式的省略規則

在Lambda標準格式的基礎上,使用省略寫法的規則爲:

  1. 小括號內參數的類型可以省略;
  2. 如果小括號內有且僅有一個參,則小括號可以省略;
  3. 如果大括號內有且僅有一個語句,則無論是否有返回值,都可以省略大括號、return關鍵字及語句分號。
    通過這些省略規則,可以嘗試簡化 -----使用Collections的sort方法對集合字符串排序

Stream

前言

試想一下,如果希望對集合中的元素進行篩選過濾:

篩選出集合中所有姓張的人,在到所有姓張的人中篩選名字長度是3個字的!

傳統的方法你可能會這樣

import java.util.ArrayList;
import java.util.List;

public class Demo02NormalFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三丰");
        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("張")) {
                zhangList.add(name);
            }
        } 
        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
                shortList.add(name);
            }
        } for (String name : shortList) {
            System.out.println(name);
        }
    }
}

代碼分析:
這段代碼中含有三個循環,每一個作用不同:

  1. 首先篩選所有姓張的人;
  2. 然後篩選名字有三個字的人;
  3. 最後進行對結果進行打印輸出。

每當我們需要對集合中的元素進行操作的時候,總是需要進行循環、循環、再循環。這是理所當然的麼?不是。循環是做事情的方式,而不是目的。另一方面,使用線性循環就意味着只能遍歷一次。如果希望再次遍歷,只能再使用另一個循環從頭開始。

當我們使用Stream後的代碼

import java.util.ArrayList;
import java.util.List;
public class Demo03StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三丰");
        list.stream()
                .filter(s -> s.startsWith("張"))
                .filter(s -> s.length() == 3)
                .forEach(System.out::println);
    }
}

我丟!怎麼可以這麼簡單 ┗( ▔, ▔ )┛
直接閱讀代碼的字面意思就可以完美展示這段代碼的功能:獲取流、過濾姓張、過濾長度爲3、逐一打印。

獲取流方式

獲取的方法比較簡單

  • 所有的 Collection 集合都可以通過 stream 默認方法獲取流;
  • Stream 接口的靜態方法 of 可以獲取數組對應的流

java.util.Collection 接口中加入了default方法 stream 用來獲取流,所以其所有實現類均可獲取流。
List集合舉例

List<String> list = new ArrayList<>();

Stream<String> stream1 = list.stream();

of 方法的參數其實是一個可變參數,所以支持數組。
舉例

String[] array = { "張無忌", "張翠山", "張三丰", "張一元" };
Stream<String> stream = Stream.of(array);

常用方法

終結方法:
    返回值類型不再是 Stream 接口自身類型的方法,因此不再支持類似 StringBuilder 那樣的鏈式
調用。終結方法包括 count 和 forEach 方法

非終結方法:
    返回值類型仍然是 Stream 接口自身類型的方法,因此支持鏈式調用。(除了終結方法外,其
餘方法均爲非終結方法。)

方法名 方法作用 方法種類 是否支持鏈式調用
count 統計個數 終結
forEach 逐一處理 終結
filter 過濾 函數拼接
limit 取用前幾個 函數拼接
skip 跳過前幾個 函數拼接
concat 組合 函數拼接

來個綜合案例使用一下這些方法

/**
 * 現在有兩個 ArrayList 集合存儲隊伍當中的多個成員姓名,依次進行以下若干操作步驟:
 * 1. 第一個隊伍只要名字爲3個字的成員姓名;
 * 2. 第一個隊伍篩選之後只要前3個人;
 * 3. 第二個隊伍只要姓張的成員姓名;
 * 4. 第二個隊伍篩選之後不要前2個人;
 * 5. 將兩個隊伍合併爲一個隊伍;
 * 7. 打印整個隊伍的信息。
 */
class Test{
    public static void main(String[] args) {
        //第一個大部隊
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1,"張三","李四","王老五","趙四兒");

        //第二個大部隊
        ArrayList<String> list2 = new ArrayList<>();
        Collections.addAll(list2,"胡歌","李易峯","彭于晏","蔡徐坤");

        //第一個隊伍只要名字爲3個字的成員姓名;  第一個隊伍篩選之後只要前3個人;
        Stream<String> stream1 = list1.stream();
        Stream<String> streamOne = stream1.filter(s -> s.length() == 3).limit(3);



        //第二個隊伍只要姓張的成員姓名;第二個隊伍篩選之後不要前2個人;
        Stream<String> stream2 = list2.stream();
        Stream<String> streamTwo = stream2.filter(s -> s.startsWith("張")).skip(2);

        //將兩個隊伍合併爲一個隊伍;  打印整個隊伍的信息。
        Stream.concat(streamOne,streamTwo).forEach(s -> System.out.println(s));

    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章