基礎——lambda表達式

lambda表達式

在這裏插入圖片描述
說到了Lambda就不得不說函數式編程。在數學中函數f(x)司空見慣,函數要求有輸入量也有輸出量並且還有表達式,簡單地說就是“拿什麼東西做什麼事情最後輸出我們想要的結果”。然而,在java中面向對象編程強調“必須通過對象的形式來做事情”,如此函數式編程與面向對象編程便區別了出來,前者強調“做什麼”,後者強調“以什麼形式做”。就好像有人說去日本,函數式編程就說一句去日本,而面向對象編程便要考慮是坐飛機去日本還是開船去日本。

lanmbda表達式就是函數式編程的一種體現,它強調的是做什麼,而不講究以什麼形式去做。使得編程的關注點聚焦於方法。

lambda表達式的形式

在Java8以前的java代碼中,經常使用內部匿名類的形式作爲接口實現類對象參數

	new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("任務B執行了...");
            }
        }).start();

這樣的代碼看起來很繁瑣,如果使用了Lambda後

	new Thread(
           () -> {
                System.out.println("任務C執行了...");
            }
        ).start();

從代碼結構中就可以看出規律,lanmbda表達式的書寫格式就是:
只需要按照匿名內部類的形式寫出代碼,然後保留抽象方法的()和{},在()和{}之間添加一個->
因此lambda表達式的組成就是:() -> {},其中()中類型可以省略,要是隻有一個參數()也可以省略;{}中如果只有一行語句,那麼return和末尾分號都可以省略;->是絕對不能省略的。

lambda表達式的使用前提

  1. 使用lambda代替的是函數式接口抽象方法的實現,也就是要求接口中有且僅有一個抽象方法。
  2. 必須要有方法使用接口作爲參數。也就是方法的參數或局部變量類型必須爲Lambda對應的接口類型,才能使用Lambda作爲該接口的實例。

Java 8中專門爲函數式接口引入了一個新的註解: @FunctionalInterface ,專門用來檢測是不是函數式接口

(重點!)常用函數式接口

1、Comparator:比較型接口,用於兩個對象的比較,其中抽象方法compare接受兩個參數o1,o2,如果return o1 - o2表示升序,如果return o2 - o1表示降序(o1和o2都不是引用類型對象)

 		Person[] arr = new Person[]{
            new Person("ttt",12),
                new Person("qw",24),
                new Person("rr",38),
                new Person("cc",45),
                new Person("uu",38),
        };

		// 匿名類寫法
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });
		
		// lambda標準寫法
        Arrays.sort(arr, (Person o1, Person o2) -> {
                return o2.getAge() - o1.getAge();
            });
       
       // lambda簡化寫法
       Arrays.sort(arr,(o1, o2) -> o1.getAge() - o2.getAge());

2、Conusmer:消費型接口,給他的抽象方法accept一個參數,就叫消費一個對象。

	public static void main(String[] args) {
        String str = "Hello World";
        // 給一個str對象消費掉
        // 匿名內部類的形式
        method(str, new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        // lanmbda標準形式
        method(str,(String s)->{
            System.out.println("長度:" + s.length());
        });
        // lambda簡化形式
        method(str,s -> System.out.println("全部大寫" + s.toUpperCase()));
    }

    public static void method(String str,Consumer<String> consumer) {
        // 調用抽象方法
        consumer.accept(str);
    }

3、Predicate:判斷型接口,返回一個boolean類型作爲判斷結果。

	public static void main(String[] args) {
        String str = "Helloworld";
        // 匿名內部類對象的方式
        method(str, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //判斷字符串長度是否大於5
                return s.length()>5;
            }
        });
        // lambda表達式的標準方式
        method(str,(String s)->{
            return s.length()>5;
        });
        // lambda表達式的簡化形式
        method(str,s->s.length()>5);
    }
    /**
     * 定義方法,使用函數式接口Predicate作爲參數
     */
    public static void method(String str,Predicate<String> p){
        boolean result = p.test(str);
        System.out.println(result);
    }

Stream流

Java8以後,引入了Stream,此流非彼流,它本質上強調的是工作流程,用於解決集合類中既有的弊端,比如在遍歷集合的同時要做一些篩選過濾。
在這裏插入圖片描述

一、展示什麼叫優雅

Stream綜合練習

  1. 第一個隊伍只要名字爲3個字的成員姓名;
  2. 第一個隊伍篩選之後只要前3個人;
  3. 第二個隊伍只要姓張的成員姓名;
  4. 第二個隊伍篩選之後不要前2個人;
  5. 將兩個隊伍合併爲一個隊伍;
  6. 打印整個隊伍的姓名信息。
 public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪麗熱巴");
        one.add("宋遠橋");
        one.add("蘇星河");
        one.add("老子");
        one.add("莊子");
        one.add("孫子");
        one.add("洪七公");



        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("張無忌");
        two.add("張三丰");
        two.add("趙麗穎");
        two.add("張二狗");
        two.add("張天愛");
        two.add("張三");

        Stream.concat(one.stream().filter(s -> s.length() == 3).limit(3)
                ,two.stream().filter(s -> s.startsWith("張")).skip(2))
                .forEach(s-> System.out.println(s));

    }

三行代碼完成上面的需求!過程清晰明瞭!

二、獲取Stream對象

1、通過Collection 集合

public default Stream<E> stream​() 方法獲取,因此Collection的實現類都能獲取流對象。

  • 注意:Map集合的實現類要通過Stream方式操作的話只能先獲取key集合、value集合或者entry集合,通過Set集合來進行操作。

2、Stream接口的靜態方法

static <T> Stream<T> of​(T… values) ,可以傳入數組來獲取流對象。

三、Stream常用方法

1、filter方法

Stream <T> filter(Predicate predicate) : 按照方法參數predicate對Stream流對象中的元素進行過濾,並返回新的Stream流對象

2、limit方法和skip方法

Stream<T> limit(long n): 獲取流對象中的前n個元素,返回新的Stream流對象

Stream<T> skip(long n): 跳過流對象中的前n個元素,返回新的Stream流對象

3、contact方法

static <T> Stream<T> concat(Stream<T> a, Stream<T> b): ,把兩個流對象a和b合併成一個流對象並返回新的流對象

4、count方法

long count() : 終結方法,調用此方法後,Stream流對象將不可以繼續使用

5、forEach方法

void forEach(Consumer<T> action): 終結方法,調用此方法後,Stream流對象將不可以繼續使用
該方法接收一個Consumer接口函數,會將每一個流元素交給該函數進行處理,不保證順序.

使用過程可能拋出的異常

Exception in thread “main” java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at com.lambda.DemoFilter.main(DemoFilter.java:16)

這是重複使用了已經用過的Stream的對象而導致的問題,因爲每個Stream的對象只能用一次。

總結:

1、終結方法:count、forEach,返回值都不是Stream對象,不能再鏈式調用
2、流對象只能被使用一次

總結

Lambda表達式簡化了Java編程的複雜性,爲函數式接口編程提供了一個面向函數編程的方式,不用再書寫繁瑣的匿名內部類。

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