Java 8 流特性和 Lambda 表示式
Java8 主要的改變是為集合框架增加了流的概念,提高了集合的抽象層次。相比於舊有框架直接運算元據的內部處理方式,流+高階函式的外部處理方式對資料封裝更好。同時流的概念使得對併發程式設計支援更強。
在語法上Java8提供了Lambda表示式來傳遞方法體,簡化了之前方法必須藏身在不必要的類中的繁瑣。Lambda表示式體現了函數語言程式設計的思想,即一個函式亦可以作為另一個函式引數和返回值,使用了函式作引數/返回值的函式被稱為高階函式。
1. Lambda表示式
Java 被詬病為繁瑣的地方就在於不支援傳遞方法,Java中的方法必須依賴類存在,也不能將方法作為引數或返回值,這是與python等語言相比的弱勢。Java 8中使用新特性Lambda表示式來改善這一點。
1.1 使用示例
以Runnable介面為例,如果需要執行一個執行緒,實際只需要run()方法中的程式碼塊,但形式上必須要先製造一個Runnable介面實現類(通常是匿名內部類)。
使用Lambda表示式僅僅需要一行程式碼,達到傳遞run方法的效果,而不必定義匿名內部類。
new Thread(()->System.out.println("Lambda")).start();
1.2 型別引數推斷機制(Type Argument Inference)
Lambda表示式之所以能夠做如此簡化得益於Java的型別引數推斷機制。所有省略的內容都可以由編譯器通過上下文推斷出來。
型別推斷機制在Java中的應用廣泛,例如陣列型別確定,Java7引入的菱形操作符等等。
型別引數推斷機制要推斷的是Lambda表示式的目標型別,往往需要與Java的過載解析機制配合。其解析規則是
- 只有一個可能目標型別時,由響應函式介面裡的引數型別推導得出
- 有多個可能目標型別,選擇最具體的型別
- 有多個可能目標型別但無法明確最具體型別,則編譯報錯
1.3 函式介面(Functional Interface)
一個方法可以抽象成函式介面。函式介面類似於一個黑箱,只需要關注其引數和返回值型別,函式介面中只有單方法。Runnable的函式介面如下:
可以看到這是一個空介面。可以用它代表所有引數和返回值都為空的方法。
Java8中定義若干函式介面(位於包java.util.function)。
介面 | 引數 | 返回型別 | 示例 |
---|---|---|---|
Pridicate<T> | T | boolean | 條件判斷 |
Consumer<T> | T | void | 消費者 |
Function<T, R> | T | R | 再加工 |
Supplier<T> | void | T | 供應者 |
BinaryOperator<T> | <T,T> | T | 求和 |
UnaryOperator<T> | T | T | 邏輯非 |
以Pridicate函式介面為例,這是一個泛型介面,引數可以是任意型別,返回值是boolean型別,代表根據數值作判斷的一類方法。
1.4 並非語法糖
從型別推斷的角度看很容易覺得Lambda表示式是和泛型,裝箱等機制一樣的語法糖,編譯器在背後補全了省略資訊,但實際上並非如此。
class Apple{ public String toString() {return "apple";}; Runnable r1 = ()->{System.out.println(this);}; Runnable r2 = new Runnable() { public void run() { System.out.println(this); } }; } -------------------- //執行兩個執行緒得到的結果是 apple Day0917.Apple$1@22e90474
正常的匿名內部類中 this關鍵字 指向內部類物件自身,同時將生成Apple$1.class檔案。
Lambda表示式中this所指向的則是外部類物件,並不會生成內部類class檔案,這說明Lambda表示式並不是語法糖,它沒有產生一個內部類,也沒有引入一個新的作用域。
Lambda與內部類相同之處在於其內部所定義的變數均為final或既成事實上的final.
1.5 預設方法
Java8最重要的改變就是對類庫的改造,使得介面中方法可以擁有程式碼體。這種定義在介面中的包含方法體的方法,需要用default修飾,稱之為預設方法。
interface Apple{ default void show(){ System.out.println("interface"); } } class MyApple implements Apple{ @Override public void show() { Apple.super.show(); } }
如果實現類中重寫了預設方法,則介面中預設方法就被覆蓋了。如果兩個介面定義了相同的預設方法,則實現類中可以通過指定全稱來確定使用哪個父類的方法。
1.6 方法引用
如果將匿名內部類改造為Lambda表示式是偷懶的話,那方法引用則是懶到連Lambda表示式都不想寫了。
在之前,我們知道Lambda表示式可以作為函式引數和返回值,表示傳遞一個方法。方法引用就是使用 ClassName::MethodName 的形式來指定方法。故而方法引用與Lambda表示式完全同源同種,可以相互替代。
//1,建立一個字串 String::new //2.建立一個字串陣列 String[]::new
注意 lambda表示式與方法引用表示的是方法本身,將要被用過高階函式的引數/返回值,並不能單獨使用。
2. 流stream
任務:建立一個姓名集合,要求出所有初始字母為a的人的總數目。使用流處理的程式碼如下:
ArrayList<String> person = new ArrayList<>(); ----init---- //1.由集合獲得流物件 Stream<String> steam = person.stream(); //2.對流物件進行過濾和統計 steam.filter((s)->s.startsWith("a")) //1.流過濾 .count(); //2.計算流物件中元素數目
使用函式介面(形式上表現為Lambda表示式)作為引數和返回值的函式就是所謂的高階函式,如此處的filter,其引數為函式介面Predicate,亦可以理解為一個介面為 T—>boolean 的方法。
上述示例中為流物件的高階函式傳入一個函式介面Predicate,避免了直接處理集合中的資料物件。示例展示了流使用的通用格式:
- 獲得流物件Stream
- 對流物件Stream進行惰性求值,返回值仍然是一個Stream物件。
- 對流物件Stream進行及早求值,返回值不在是一個Stream物件。
2.1常見高階函式
1.collect方法
collect方法屬於一個及早求值方法,負責將流物件轉換成其他資料結構,如列表,集合,值等。這項工作由收集器Collector完成。java8為此提供了Collectors工具類。
1.1 轉換成集合
List<Person> list = stream.collect(Collectors.toList()); List<Person> arraylist = stream.collect(Collectors.toCollection(ArrayList::new)); Set<Person> set = stream.collect(Collectors.toSet()); Set<Person> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
使用Collectors.toList()將流物件轉換成集合時並不需要指定具體型別,Java預設選擇了實現型別,如果要自己指定,可以使用Collectors.toCollection(ArrayList::new),其引數ArrayList::new就是上文中的方法引用,表示一個建立ArrayList物件的方法,ArrayList就是想要轉換成的資料型別;
1.2 轉換成值
//1.獲得最大最小值 Function<Person, Integer> getLevel = p->p.age; Comparator<Person> comparator = Comparator.comparing(getLevel); stream.collect(Collectors.maxBy(comparator)); stream.collect(Collectors.minBy(comparator)); //2.獲得平均值 ToIntFunction<Person> getAverage = p->p.age; stream.collect(Collectors.averagingInt(getAverage));
1.3 資料分塊
將流物件按某種條件分成兩部分
Predicate<Person> isTang = p->p.country.equals(Country.Tang); stream.collect(Collectors.partitioningBy(isTang));
1.4 資料分組
Function<Person, Integer> country= p -> p.country.ordinal(); stream.collect(Collectors.groupingBy(country));
分塊和分組看似相同,但意義不同,分塊使用判斷作為方法,只能將流分成兩塊;分組則靈活的多。
1.5 字串
stream.map(Person::getName).collect(Collectors.joining("/", "[", "]"));
1.6 合併收集器
stream.collect(Collectors.groupingBy(country,Collectors.counting()));
2.map
map是一個惰性求值方法。函式介面為Function<T, R>函式介面,負責將資料從一個型別轉換為另一個型別;高階函式map的作用就是將資料從一個流轉換為另一個流。
3.filter
filter 是一個惰性求值方法。函式介面為Pridicate<T>,此方法負責對資料進行判斷,filter高階函式負責根據判斷結果對流進行過濾。
4.flatMap系列
flatMap 是一個惰性求值方法。其引數亦為Function<T, R>,將多個流組合為一個流。
//1.a1,a2是兩個列表,map處理後仍是兩個列表 Stream.of(a1,a2).map(s->s) ------------- [1, 2, 3, 4] [] //2.flatMap將二者合併為一個流 Stream.of(a1,a2).map(s->s) .flatMap(s->s.stream()) ------------- 1234
看原始碼可知,flatMap中函式介面Function的輸出型別為Stream<R>。
5.max/min
屬於一個及早求值方法。需要傳入一個Comparator函式介面,Java8提供了Comparator.comparing方法獲得該函式介面的實現,該靜態方法是介面的靜態方法,獲得一個函式返回一個Comparator物件。
min(Comparator.comparing(s->s.toString()));
max/min的返回值是 Optional,代表一個或有或無的值,主要是用來取代萬惡的null值;使用get方法可以獲取其值。
6.reduce
屬於一個及早求值方法。意為流資料的累加,有兩個版本。
//1.無初始值累加 T t = person.stream().reduce((a,b)->a+b); //2.帶初始值累加 Optional<T> t = person.stream().reduce("1",(a,b)->a+b);
7. foreach
屬於一個及早求值方法,用來遍歷流物件。
總而言之,Java8中流物件的引入使得可以在更高的層次上對集合進行處理,使得抽象的方法和具體的行為邏輯分離開來,也加強了資料的封裝性,另一個好處是對併發的支援更強,以後再補充。
相關文章
- java8特性-lambda表示式Java
- Java8新特性——從Lambda表示式到Stream流Java
- Java 8新特性(一):Lambda表示式Java
- Java8新特性(一)-Lambda表示式Java
- Java8新特性(1):Lambda表示式Java
- java8 新特性之Lambda 表示式Java
- java8的新特性之lambda表示式和方法引用Java
- Java 8 Lambda 表示式Java
- java 8 lambda表示式Java
- Java8-Lambda表示式Java
- Java8特性詳解 lambda表示式(一):使用篇Java
- Java8特性詳解 lambda表示式(二):流式處理中的lambdaJava
- Java8特性詳解 lambda表示式(三):原理篇Java
- Java8的Lambda表示式Java
- java-反射,介面新特性,Lambda表示式Java反射
- ?Java8新特性之Lambda表示式,函式式介面,方法引用和default關鍵字Java函式
- Java 8: Lambda表示式增強版Comparator和排序Java排序
- 《Java 8 in Action》Chapter 3:Lambda表示式JavaAPT
- Java 8 lambda 表示式10個示例Java
- Java8中的Lambda表示式Java
- 好程式設計師分享java8新特性之Lambda表示式程式設計師Java
- java8學習:lambda表示式(2)Java
- java8學習:lambda表示式(1)Java
- Java | Lambda表示式Java
- Lambda表示式(Java)Java
- Java Lambda表示式Java
- 【Java8新特性】Lambda表示式基礎語法,都在這兒了!!Java
- Java8-增強版Comparator和排序之Lambda表示式Java排序
- Java 8 Lambda表示式一看就會Java
- Java8 Lambda表示式、Optional類淺析Java
- Java的Lambda表示式Java
- Java之lambda表示式Java
- Java8新特性 - LambdaJava
- JDK 1.8 新特性之Lambda表示式JDK
- JDK1.8新特性--Lambda表示式JDK
- jdk1.8新特性:Lambda表示式JDK
- .NET3.5新特性,Lambda表示式
- java8新特性之函式式介面、lambda表示式、介面的預設方法、方法和建構函式的引用Java函式
- Java 8:一文帶你掌握 Lambda 表示式Java