Java系列筆記第九章:Stream流與方法引用

第九章:Stream流與方法引用

1. Stream流

1.1 Stream簡介

JDK 1.8開始引入。

流不一定是IO流。得益於lambda編程帶來的函數式編程,引入Stream的概念,用於解決集合類的弊端,簡化操作。

比如List的遍歷,只能用for循環。我們關注於做什麼(循環體), 而不是怎麼做(for循環語法)。

示例:

我們有一個List,先篩選出姓張的人,再選出名字長度是3的人,再輸出。

  • 使用list.stream()方法轉換爲Stream流。
  • 流有filter方法,傳進去一個Predicate接口的lambda表達式進行篩選。
  • 流有forEach接口,傳進去消費者接口,將數據輸出。
list.stream()
    .filter((s -> s.startsWith("張")))
    .filter(s -> s.length() == 3)
    .forEach(System.out::println);

拼接流式模型:建立一個生產線,按照生產線來生產商品。每個操作都是一個流,從一個流的模型可以轉換爲另一個流的模型。集合的數據並沒有被實時操作,只有最終執行時纔會開始執行,這也是lambda延遲執行的特點。

Stream並不會存儲元素,而是按需計算。

數據來源可以是集合或者數組。


Stream有兩個基礎特徵:

  • Pipelining: 中間操作都會返回流對象本身,這樣多個操作可以串聯成一個管道。這樣就可以對操作進行優化,比如延遲執行短路

  • 內部迭代: Stream可以直接調內部迭代的方法。


  • filter
  • map
  • skip
  • count
  • forEach

1.2 獲取流

  1. 所有的Collection集合均可通過stream()方法獲取流。

  2. Stream接口的靜態方法of也可以。

    • static <T> Stream<T> of(T...values)
  3. Map<K, V>獲取流的方法:

    • 先獲取Map.Entry<K, V>,存入Set裏,再轉換爲流。
  4. 數組轉換爲流:

    • Stream.of(一個數組)。
    • Stream.of(1, 2, 3, 4, 5)。

1.3 流的常用方法

流有兩類方法:

  • 延遲方法

    • 返回值是Stream流對象,所以可以鏈式調用。
  • 終結方法

    • count()和forEach()方法是終結方法。

1.3.1 forEach方法

該方法接收一個Consumer接口,會將每一個元素交給接口去處理。

調用之後就會將流終結。

一般用來遍歷流。

list.stream().forEach((str) -> System.out.println(str));

優化爲方法引用:list.stream().forEach(System.out::println);

1.3.2 filter方法

這是個過濾方法,傳遞進來一個Predicate接口,對傳進來的數據進行判斷。如果滿足條件,這個元素將被放進流,否則元素會被捨棄。

1.3.3 map方法

這是個映射方法,傳遞進來一個Function接口,將當前流中的數據轉換爲另一種類型的數據。

list.stream().map(s -> Integer.parseInt(s));

優化爲方法引用:list.stream().map(Integer::parseInt);

1.3.4 count方法

返回一個long類型的數據,統計Stream中元素的個數。

調用之後就會將流終結。

1.3.5 limit方法

截取前n個元素,n是long類型。如果n大於流中元素的長度,就會返回全部元素組成的流。

這是個延遲方法,只是對流中的元素進行截取,返回一個新的流對象。

list.stream().limit(2); 返回list中前兩個元素組成的流。

1.3.5 skip方法

跳過前n個元素。這是個延遲方法,只是對流中的元素進行截取,返回一個新的流對象。如果n大於流中元素的個數,就會返回一個空的流。

1.3.6 contract方法

組合兩個流。Stream流的靜態方法,可以將兩個流合併成一個流。
Stream.concat(stream1, stream2);

1.4 Stream注意事項

Stream流屬於管道流,只能被使用一次。第一個流調用完畢方法後,數據就會轉到下一個Stream上。此時第一個Stream流已被使用完畢,不能再使用。

2. 方法引用

2.1 簡介

我們傳遞進去的lambda表達式是一種解決方案:拿什麼參數、做什麼操作。如果其他地方已經存在了這樣的方案,就不用重寫重複的邏輯。


示例:

接口定義:

package MethodReference;
@FunctionalInterface
public interface PrintAble {
    void print(String str);
}

測試類:

package MethodReference;

public class PrintAbleDemo {
    public static void fun(String str, PrintAble p) {
        p.print(str);
    }

    public static void main(String[] args) {
        String s = "aaa";
        fun(s, str -> System.out.println(str));
    }
}

分析:

str -> System.out.println(str)這個lambda表達式的目的是把str傳給System.out對象,調用println方法打印。這個對象和方法都是已經存在的。

所以我們可以直接使用System.out::println來輸出字符串。參數被省略。

2.2 語義分析

注意: 傳遞的參數一定要是方法引用中可以接收的類型,否則會拋異常。

2.3 通過對象名引用成員方法

如果某個類的一個方法實現了我們想要的操作,就可以使用這個類對象來調用這個方法,作爲方法引用。

接口定義:

package MethodReference.ObjectMethodReference;
@FunctionalInterface
public interface PrintAble {
    void print(String str);
}

類定義:

package MethodReference.ObjectMethodReference;

public class MethodReferObj {
    public void toUpper(String str){
        System.out.println(str.toUpperCase());
    }
}

測試:

package MethodReference.ObjectMethodReference;

public class Demo {
    public static void fun(PrintAble p){
        p.print("Hello");
    }

    public static void main(String[] args) {
        MethodReferObj obj = new MethodReferObj();
        fun(obj::toUpper);
    }
}

2.4 通過類名引用靜態成員方法

類存在,靜態成員方法存在,就可以不新建對象,直接用類::成員方法來實現方法引用。

2.5 通過super引用父類成員方法

package MethodReference.superDemo;

public class Man extends Human{
    @Override
    public void sayHello(){
        System.out.println("我是子類方法");
    }

    public void method(Greeting g){
        g.meeting();
    }

    public void show(){
        method(super::sayHello);
    }
}

存在繼承關係、父類有sayHello方法。

2.6 通過this引用本類的方法

package MethodReference.superDemo;

public class Man extends Human{
    @Override
    public void sayHello(){
        System.out.println("我是子類方法");
    }

    public void method(Greeting g){
        g.meeting();
    }

    public void show(){
        method(this::sayHello);
    }
}

就是把2.5中的super改爲this。

2.7 類的構造器引用

package MethodReference.ConstructReferDemo;

public class Demo {
    public static void printName(String s, PersonBuilder p){
        Person build = p.build(s);
        System.out.println(build.getName());
    }

    public static void main(String[] args) {
        String name = "張三";
        
        //傳統lambda表達式
        printName(name,(s)->new Person(s));
        
        //方法引用,類名::new
        printName(name,Person::new);
    }
}

2.8 數組的構造器引用

int[]::new

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