
1. Lambda表達式
1.1 Lambda表達式的好處
Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解為是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)
使用它可以寫出更簡潔、更靈活的代碼。作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升
Lambda表達式的本質是:作為函數式接口的實例
1.2 Lambda表達式舉例
//導入的包有;import org.junit.Test;import java.util.Comparator;
public class LambdaTest1 {
@Test
public void test1(){
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("我愛北京天安門");
}
};
r1.run();
//Lambad表達式寫法
Runnable r2 = () -> System.out.println("我愛北京天安門");
r2.run();
}
@Test
public void test2(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
int compare1 = com1.compare(12,21);
System.out.println(compare1);
//Lambda表達式
Comparator<Integer> com2 = (o1,o2) ->Integer.compare(o1,o2);
int compare2 = com2.compare(12,21);
System.out.println(compare2);
//方法引用
Comparator<Integer> com3 = Integer :: compare;
int compare3 = com3.compare(12,21);
System.out.println(compare3);
}
}
1.3 Lambda表達式的使用
舉例:(o1,o2) -> Integer.compare(o1,o2)
格式:
- ->:Lambda操作符或箭頭操作符
- ->左邊:Lambda形參列表(其實就是接口中的抽象方法的形參列表)
- ->右邊:Lambda體(其實就是重寫的抽象方法的方法體)
語法格式:
- 語法格式一:無參,無返回值

- 語法格式二:Lambda需要一個參數,但是沒有返回值

- 語法格式三:數據類型可以省略,因為可由編譯器推斷得出,稱為“類型推斷”

- 語法格式四:Lambda若只需要一個參數時,參數的小括號可以省略

- 語法格式五:Lambda需要兩個或以上的參數,多條執(zhí)行語句,并且可以有返回值

- 語法格式六:當Lambda體只有一條語句時,return與大括號若有,都可以省略

總的來說,語法格式為:
- ->左邊:lambda形參列表的參數類型可以省略(類型推斷),如果形參列表只有一個參數,其一對()也可省略
- ->右邊:lambda體應該使用一對{}包裹,如果lambda體只有一條執(zhí)行語句(可能是return語句),省略這一對{}和return關鍵字
//導入的包有:import org.junit.Test;import java.util.Comparator;import java.util.function.Consumer;
public class LambdaTest2 {
@Test
public void test1(){
//語法格式一:無參,無返回值
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("我愛北京天安門");
}
};
r1.run();
System.out.println("***************************");
//Lambad表達式寫法
Runnable r2 = () -> {
System.out.println("我愛北京天安門");
};
r2.run();
}
@Test
public void test2(){
//語法格式二:Lambda需要一個參數,但是沒有返回值
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("今天是個好日子");
System.out.println("***************************");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("今天是個好日子");
}
@Test
public void test3(){
//語法格式三:數據類型可以省略,因為可由編譯器推斷得出,稱為“類型推斷”
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("今天是個好日子");
System.out.println("***************************");
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("今天是個好日子");
}
@Test
public void test4(){
//語法格式四:Lambda若只需要一個參數時,參數的小括號可以省略
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("今天是個好日子");
System.out.println("***************************");
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("今天是個好日子");
}
@Test
public void test5(){
//語法格式五:Lambda需要兩個或以上的參數,多條執(zhí)行語句,并且可以有返回值
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println("*******************");
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
}
@Test
public void test6(){
//語法格式六:當Lambda體只有一條語句時,return與大括號若有,都可以省略
Comparator<Integer> com1 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println("*******************");
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
}
}
2. 函數式(Functional)接口
- 只包含一個抽象方法的接口,稱為函數式接口
- 你可以通過 Lambda 表達式來創(chuàng)建該接口的對象
- 我們可以在一個接口上使用@FunctionalInterface注解,這樣做可以檢查它是否是一個函數式接口
- 以前用匿名實現類表示的現在都可以用Lambda表達式來寫
- 在java.util.function包下定義了Java 8 的豐富的函數式接口


3. 方法引用與構造器引用
3.1 方法引用
使用情景:當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用
方法引用本質:其本質為Lambda表達式,而Lambda表達式是為函數式接口的實例,所以方法引用,也是函數式接口的實例
使用格式:類(對象) :: 方法名
-
具體使用情況:
- 對象 :: 非靜態(tài)方法(實例方法)

- 類 :: 靜態(tài)方法

- 類 :: 非靜態(tài)方法

- 使用要求:要求接口中抽象方法的形參列表和返回值類型,與方法引用的方法的形參列表和返回值類型相同,但當類調用非靜態(tài)方法時,可以不相同
3.2 構造器引用與數組引用
構造器引用
和方法引用類似,函數式接口的抽象方法的形參列表和構造器的形參列表一致。抽象方法的返回值類型即為構造器所屬類的類型

數組引用
可以把數組看做是一個特殊的類,則寫法與構造器引用一致

4. 強大的Stream API
4.1 Stream API概述
什么是Stream API
- Stream API ( java.util.stream) 把真正的函數式編程風格引入到Java中
- Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執(zhí)行非常復雜的查找、過濾和映射數據等操作
- 使用Stream API 對集合數據進行操作,就類似于使用 SQL 執(zhí)行的數據庫查詢
- Stream 和 Collection 集合的區(qū)別:Collection 是一種靜態(tài)的內存數據結構,而 Stream 是有關計算的。前者是主要面向內存,存儲在內存中,后者主要是面向 CPU,通過 CPU 實現計算
- 集合講的是數據,Stream講的是計算
Stream的操作步驟
- 創(chuàng)建Stream:一個數據源(如:集合、數組),獲取一個流
- 中間操作:一個中間操作鏈,對數據源的數據進行處理
- 終止操作(終端操作) :一旦執(zhí)行終止操作,就執(zhí)行中間操作鏈,并產生結果。之后,不會再被使用

注意事項
- Stream 自己不會存儲元素
- Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream
- Stream 操作是延遲執(zhí)行的。這意味著他們會等到需要結果的時候才執(zhí)行
4.2 Stream的實例化
Stream的實例化有四種方式:
- 創(chuàng)建Stream方式一:通過集合
- 創(chuàng)建Stream方式二:通過數組
- 創(chuàng)建Stream方式三:通過Stream的of()
- 創(chuàng)建Stream方式四:創(chuàng)建無限流(不常使用)
//導入的包有:import org.junit.Test;import java.util.Arrays;import java.util.List;import java.util.stream.IntStream;import java.util.stream.Stream;
public class StreamTest {
//創(chuàng)建Stream方式一:通過集合
@Test
public void test1(){
//先創(chuàng)建一個集合
List<Employee> employees = EmployeeData.getEmployees();
//default Stream<E> stream() : 返回一個順序流
Stream<Employee> stream = employees.stream();
//default Stream<E> parallelStream() : 返回一個并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
//創(chuàng)建Stream方式二:通過數組
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5,6};
//調用Array類的static <T> Stream<T> stream(T[] array): 返回一個流
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Jerry");
Employee[] arr1 = new Employee[]{e1,e2};
//常見的數組可以,自定義的數組arr1也行
Stream<Employee> stream1 = Arrays.stream(arr1);
}
//創(chuàng)建Stream方式三:通過Stream的of()
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
}
}
4.3 Stream的中間操作
- 篩選與切片

//篩選與切片
@Test
public void test1(){
List<Employee> list = EmployeeData.getEmployees();
//filter(Predicate p):接收Lambda,從流中排除某些元素
Stream<Employee> stream = list.stream();
//查詢員工表中薪資大于7000的員工信息
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
//limit(n):截斷流,使其元素不超過給定數量
list.stream().limit(3).forEach(System.out::println);
//skip(n):跳過元素,返回一個扔掉了前n個元素的流,若流中元素不足n個,則返回一個空流
list.stream().skip(3).forEach(System.out::println);
//distinct():篩選,通過流所生成元素的hashCode()和equals()去除重復元素
list.stream().distinct().forEach(System.out::println);
}
- 映射

//映射
@Test
public void test2(){
//接收一個函數作為參數,該函數會被應用到每個元素上,并將其映射成一個新的元素
List<String> list = Arrays.asList("aa","bb","cc","dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
}
- 排序

//排序
@Test
public void test3(){
//sorted():自然排序
List<Integer> list = Arrays.asList(12,45,-8,2,56,34);
list.stream().sorted().forEach(System.out::println);
//使用自然排序的條件是:所指定泛型要實現Comparable接口
//sorted(Comparator com):定制排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()))
.forEach(System.out::println);
}
4.4 Stream的終止操作
- 匹配與查找


//匹配與查找
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//allMatch(Predicate p):檢查是否匹配所有元素:所有員工年齡是否大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
//anyMatch(Predicate p):檢查是否至少匹配一個元素:是否存在員工工資大于1000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 1000);
System.out.println(anyMatch);
//noneMatch(Predicate p):檢查是否沒有匹配所有元素:是否存在員工姓“雷”
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch);
//findFirst():返回第一個元素
Optional<Employee> employee = employees.stream().findFirst();
System.out.println(employee);
//findAny():返回當前流中的任意元素
Optional<Employee> employee1 = employees.stream().findAny();
System.out.println(employee1);
//count():返回流中元素總個數
long count = employees.stream().count();
System.out.println(count);
//max(Comparator c):返回流中最大值:返回最高的工資
Optional<Double> maxSalary = employees.stream().map(e -> e.getSalary()).max(Double::compare);
System.out.println(maxSalary);
//min(Comparator c):返回流中最小值:返回最低的工資
Optional<Employee> minSalary = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(minSalary);
//forEach(Consumer c):內部迭代
// 使用 Collection 接口需要用戶去做迭代,稱為外部迭代
// 相反,Stream API 使用內部迭代——它幫你把迭代做了
employees.stream().forEach(System.out::println);
}
- 歸約

//規(guī)約
@Test
public void test2(){
//reduce(T iden, BinaryOperator b) 可以將流中元素反復結合起來,得到一個值。返回 T
//練習:計算1-10的自然數的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
//reduce(BinaryOperator b) 可以將流中元素反復結合起來,得到一個值。返回 Optional<T>
//練習:計算公司所有員工工資的總和
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
Optional<Double> sumMoney = salaryStream.reduce((d1, d2) -> d1 + d2);
System.out.println(sumMoney);
}
- 收集

//收集
@Test
public void test3(){
//collect(Collector c)將流轉換為其他形式。接收一個 Collector接口的實現,用于給Stream中元素做匯總的方法
//練習:查找工資大于6000的員工,結果返回為一個List或Set
//結果返回為一個List
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> list = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
list.forEach(System.out::println);
//結果返回為一個Set
Set<Employee> set = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
list.forEach(System.out::println);
}
5. Optional類
Optional< T > 類(java.util.Optional) 是一個容器類,它可以保存類型T的值,代表這個值存在。或者僅僅保存null,表示這個值不存在。
原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。并且可以避免空指針異常
Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測:
-
創(chuàng)建Optional類對象的方法:
- Optional.of(T t):創(chuàng)建一個 Optional 實例,t必須非空
- Optional.empty():創(chuàng)建一個空的 Optional 實例
- Optional.ofNullable(T t):t可以為null
-
判斷Optional容器中是否包含對象:
- boolean isPresent():判斷是否包含對象
-
獲取Optional容器的對象:
- T get(): 如果調用對象包含值,返回該值,否則拋異常
- T orElse(T other) :如果有值則將其返回,否則返回指定的other對象
舉例說明Optional類:

