Java8新特性之方法引用&Stream流


Java8新特性

方法引用

前言

什么是函數式接口

  • 只包含一個抽象方法的接口,稱為函數式接口

  • 可以通過 Lambda 表達式來創建該接口的對象。(若 Lambda 表達式拋出一個受檢異常(即:非運行時異常),那么該異常需要在目標接口的抽象方法上進行聲明)。

  • 可以在一個接口上使用 @FunctionalInterface 注解,這樣做可以檢查它是否是一個函數式接口。同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。

如何理解函數式接口

  • 在函數式編程語言當中,函數被當做一等公民對待。在將函數作為一等公民的編程語言中,Lambda表達式的類型是函數。但是在Java8中,有所不同。在Java8中,Lambda表達式是對象,而不是函數,它們必須依附於一類特別的對象類型——函數式接口。
  • 簡單的說,在Java8中,Lambda表達式就是一個函數式接口的實例。這就是Lambda表達式和函數式接口的關系。也就是說,只要一個對象是函數式接口的實例,那么該對象就可以用Lambda表達式來表示。
  • 所以以前用匿名實現類表示的現在都可以用Lambda表達式來寫。

方法引用

方法引用可以看做是Lambda表達式深層次的表達。換句話說,方法引用就是Lambda表達式,也就是函數式接口的一個實例,通過方法的名字來指向一個方法,可以認為是Lambda表達式的一個語法糖。

要求:實現接口的抽象方法的參數列表和返回值類型,必須與引用的方法的參數列表和返回值類型保持一致!

格式:使用操作符 :: 將類(或對象) 與 方法名分隔開來。

如下三種主要使用情況:

  • 對象實例 :: 方法名
  • 類 :: 靜態方法名
  • 類 :: 實例方法名

例如x -> System.out.println(x)等價於 System.out::println

舉例

  List<Integer> list = Arrays.asList(82,22,34,50,9);
        //原始寫法
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(list);
        
        //lambda方式
        list.sort((o1,o2) -> o2 - o1);
        System.out.println(list);
        
        //方法引用
        list.sort(Integer::compareTo);
        System.out.println(list);
/*
輸出結果
[82, 50, 34, 22, 9]
[82, 50, 34, 22, 9]
[9, 22, 34, 50, 82]
    */
// 情況一:對象 :: 實例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
	Consumer<String> con1 = str -> System.out.println(str);
	con1.accept("北京");

	System.out.println("*******************");
	PrintStream ps = System.out;
	Consumer<String> con2 = ps::println;
	con2.accept("beijing");
}

//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
	Employee emp = new Employee(1001,"Tom",23,5600);

	Supplier<String> sup1 = () -> emp.getName();
	System.out.println(sup1.get());

	System.out.println("*******************");
	Supplier<String> sup2 = emp::getName;
	System.out.println(sup2.get());

}

// 情況二:類 :: 靜態方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
	Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
	System.out.println(com1.compare(12,21));

	System.out.println("*******************");

	Comparator<Integer> com2 = Integer::compare;
	System.out.println(com2.compare(12,3));

}

//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
	Function<Double,Long> func = new Function<Double, Long>() {
		@Override
		public Long apply(Double d) {
			return Math.round(d);
		}
	};

	System.out.println("*******************");

	Function<Double,Long> func1 = d -> Math.round(d);
	System.out.println(func1.apply(12.3));

	System.out.println("*******************");

	Function<Double,Long> func2 = Math::round;
	System.out.println(func2.apply(12.6));
}

// 情況:類 :: 實例方法  (有難度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
	Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
	System.out.println(com1.compare("abc","abd"));

	System.out.println("*******************");

	Comparator<String> com2 = String :: compareTo;
	System.out.println(com2.compare("abd","abm"));
}

//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
	BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
	System.out.println(pre1.test("abc","abc"));

	System.out.println("*******************");
	BiPredicate<String,String> pre2 = String :: equals;
	System.out.println(pre2.test("abc","abd"));
}

// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
	Employee employee = new Employee(1001, "Jerry", 23, 6000);


	Function<Employee,String> func1 = e -> e.getName();
	System.out.println(func1.apply(employee));

	System.out.println("*******************");

	Function<Employee,String> func2 = Employee::getName;
	System.out.println(func2.apply(employee));


}

構造器引用

格式: 類名 :: new

與函數式接口相結合,自動與函數式接口中方法兼容。可以把構造器引用賦值給定義的方法,要求構造器參數列表要與接口中抽象方法的參數列表一致!且方法的返回值即為構造器對應類的對象

數組引用

**格式: ** type[] :: new

Function<Integer,String[]> func1 = (length) -> new String[length];
String[] arr1 = func1.apply(5);
//等價於
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);

可參考文檔

傳送門:廖雪峰

Stream API

為什么要使用Stream API

	實際開發中,項目中多數數據源都來自於Mysql,Oracle等。但現在數據源可以更多了,有MongDB,Radis等,而這些NoSQL的數據就需要Java層面去處理

Stream 和 Collection 集合的區別:Collection 是一種靜態的內存數據結構,而 Stream 是有關計算的。前者是主要面向內存,存儲在內存中,后者主要是面向 CPU,通過 CPU 實現計算。

什么是 Stream

是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。

集合講的是數據,Stream講的是計算!

注意:

  • Stream 自己不會存儲元素。

  • Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。

  • Stream 操作是延遲執行的。這意味着他們會等到需要結果的時候才執行。

Stream操作的三個步驟

  1. 創建Stream
  2. 中間操作
  3. 終止操作

創建Stream

  • Java8 中的 Collection 接口被擴展,提供了兩個獲取流的方法

    • default Stream<E> stream() : 返回一個順序流
    • default Stream<E> parallelStream() : 返回一個並行流
  • Java8 中的 Arrays 的靜態方法 stream() 可以獲取數組流

    • static <T> Stream<T> stream(T[] array)😗* 返回一個流
  • 通過Stream的of()

Stream 的中間操作

多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次性全部處理,稱為惰性求值

1-篩選與切片

2-映射

3-排序

終止操作

終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

流進行了終止操作后,不能再次使用。

  • count() 返回流中元素總數
  • max(Comparator c)返回流中最大值
  • min(Comparator c) 返回流中最小值
  • forEach(Consumer c)內部迭代(使用 Collection 接口需要用戶去做迭代,稱為外部迭代。相反,Stream API 使用內部迭代——它幫你把迭代做了)

歸約和收集

  • reduce(T iden, BinaryOperator b) 可以將流中元素反復結合起來,得到一個值。返回 T
  • collect(Collector c)將流轉換為其他形式。接收一個 Collector接口的實現,用於給Stream中元素做匯總的方法

Collectors

實例

傳送門


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM