JDK8的新特性

XmCui發表於2018-12-24

JDK8的新特性

這兩天開始看jdk8的新特性,先看了Lembda表示式和Stream,就一個感覺,優雅。
趕緊將這兩天的成果總結一下

一、Lembda表示式

Lembda表示式格式

lembda表示式是一個簡潔、可傳遞的匿名函式,實現了把程式碼塊賦值給一個變數的功能

格式就是 (parameters) -> expression ,需要注意的是:

  1. 引數可以為空 ()->xxx
  2. 引數為一個值得時候,可以省略引數的括號 x->xxx
  3. 表示式只有一行的時候不需要;,有多行的時候需要加上;
  4. 表示式可以是一個數字(直接返回這個數字),一個算式;可以是普通的一個語句(無返回,相當於void)
  5. 引數的名不能和區域性變數相同
  6. 表示式中直接呼叫的變數(不是傳入的),必須是顯示宣告為final或事實上的final型別

函式式介面

lambda表示式的使用需要藉助於函式式介面
含有且僅含有一個抽象方法的介面被稱為函式式介面

  • 需要注意的是,default關鍵字可以在介面中定義實現,
    如果一個介面含有多個實現但是隻有一個抽象方法,那麼它也屬於函式式介面,另外static方法也可以存在於函式式介面

自定義函式式介面

一般用@FunctionInterface對函式式介面來進行語義化標註

步驟:

  1. 定義一個函式式介面
    @FunctionalInterface
    public interface Append {
        int append(int a,int b);
    }
    public static void main(String[] args) {
        //對這個介面的抽象方法進行實現,並用該介面進行接收
        Append append = ((a, b) -> a + b);
        //呼叫這個方法
        int result = append.append(1, 2);
        System.out.println("result = "+result);
    }
  1. 對這個介面的抽象方法進行實現,並用該介面進行接收,
  2. 呼叫這個方法

結果: result = 3

JDK自帶的函式式介面

  1. Predicate
  2. Consumer
  3. Function<T, R> 方法
    R apoly(T t);
    執行轉換操作,輸入型別 T 的資料,返回 R 型別的結果

這三個是最重要的介面,Stream也用到這些介面,下面我強行使用這三個介面

    /*Predicate<T> 判斷*/
    Predicate<String> stringPredicate = str -> StringUtils.isBlank(str) || "error".equalsIgnoreCase(str);
    
    /*Consumer<T>*/
    Consumer<String> stringConsumer = str -> {
        if (StringUtils.isBlank(str) || "error".equalsIgnoreCase(str)) {
            System.out.println("Consumer失敗");
        }
    };
    /*Function<T,R>*/
    Function<String, String> stringStringFunction = str -> {
        if (StringUtils.isBlank(str) || "error".equalsIgnoreCase(str)) {
            return "Function失敗";
        } else {
            return "Function成功";
        }
    };
    String in = "error";
    if (stringPredicate.test(in)) {
        System.out.println("Predicate失敗");
    }
    stringConsumer.accept(in);
    System.out.println(stringStringFunction.apply(in));

方法引用

在表示式中,可以使用::更方便的呼叫方法,它會自動將引數傳入,並且將返回值返回
比如
System.out.println(傳入引數)可以轉換為System.out::println
表示式只需要呼叫一個方法就可以完成功能的時候,可以用這種方法來進一步簡化程式碼;

二.Stream流

Stream使用上面說的JDK自帶的函式式介面
Stream是一種函數語言程式設計對集合,陣列,I/O channel, 產生器generator等資料的操作方式,它有以下特點:

  1. 無儲存,Stream本身不會儲存資料
  2. 不會修改傳入的資料來源
  3. 惰性執行:Stream上的操作不會立即執行,而是會在真正需要的時候進行
  4. 可消費性:Stream只能被消費一次,類似迭代器,想要再次遍歷,必須生成新的Stream.

在學習過程中,我發現Stream某些操作之後返回的仍然是流,而有些操作返回的確實真實的被處理之後資料,Stream的操作可以據此分為兩種

  1. 中間操作 總是會惰式執行,呼叫中間操作只會生成一個標記了該操作的新stream。
  2. 結束操作 會觸發實際計算,計算髮生時會把所有中間操作積攢的操作以pipeline的方式執行,這樣可以減少迭代次數。計算完成之後stream就會失效。

總之,把Stream看做一根水管就好啦,一開始把一根根水管拼接起來(中間操作),安裝一個水龍頭(結束操作),確認有水龍頭之後水就依次通過水管,最後通過水龍頭進入下水道(不能重複使用);

Collection.stream() 或者 Collection.parallelStream() 來建立一個序列Stream或者並行Stream。

常用的Stream方法

中間操作

sorted排序

Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
這是一個有狀態的操作
用法|作用
—|—
sorted()|自然排序
sorted(Comparator.reverseOrder()) |自然逆向排序
sorted(Comparator.comparing(Student::getAge)) |通過某些元素排序
sorted(Comparator.comparing(Student::getAge).reversed()) |通過某些元素逆向排序

Comparator是比較器

map元素操作

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
用於對每個元素進行操作,並且將處理後的Stream返回
例如map(i->i*2)將所有資料進行平方操作

filter過濾

Stream<T> filter(Predicate<? super T> predicate);
入參為Predicate,lembda表示式返回值是boolean;
如果表示式為flase,則剔除資料,只留符合條件的
過濾是中間操作:Stream<Integer> integerStream = intList.stream().filter(i -> i > 4);

limit限制

Stream<T> limit(long maxSize);
限制資料的數量,這是一個有狀態的短路的操作。

distinct去重

Stream<T> distinct();
去除重複的操作,這是一個有狀態的操作

結束操作

forEach迭代

void forEach(Consumer<? super T> action);
入參是Consumer,表示式不需要返回值,方法本身返回值void,所以是結束操作
常用的方法是forEach(System.out::println);

collect存入容器

<R, A> R collect(Collector<? super T, A, R> collector);
將資料存入一個Collection或Map

toArray存入陣列

Object[] toArray();
將結果存入一個陣列中

count計數

long count();
計算流中元素的數量

max 和 min

Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
根據一個比較器找到最大或最小元素

anyMatch allMatch noneMatch 匹配

boolean anyMatch(Predicate<? super T> predicate);三個都一樣
是否至少有一個元素匹配
是否每一個元素都匹配
是否沒有元素匹配

findFirst findAny 查詢

Optional<T> findFirst();
查詢第一個元素
查詢隨機的一個元素

還有其他流

在Stream中我發現mapToInt這個方法返回值是另一個流,也有相應的方法,比較好用的就是sum()求和等,回頭再翻原始碼

常用的

  • List轉Map Map<Integer, A> aMap = aList.stream().collect(Collectors.toMap(A::getId, a -> a,(k1,k2)->k1));

  • 一個獲取隨機數的方法random.ints().limit(10).forEach(System.out::println);

未完待續

參考資料:

  1. https://blog.csdn.net/chenhao_c_h/article/details/80691284
  2. https://www.cnblogs.com/pikachu-zhaof/p/9724826.html

相關文章