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操作的三個步驟
- 創建Stream
- 中間操作
- 終止操作
創建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)
可以將流中元素反復結合起來,得到一個值。返回 Tcollect(Collector c)
將流轉換為其他形式。接收一個 Collector接口的實現,用於給Stream中元素做匯總的方法