lambda表達式
說到了Lambda就不得不說函數式編程。在數學中函數f(x)
司空見慣,函數要求有輸入量也有輸出量並且還有表達式,簡單地說就是“拿什麼東西做什麼事情最後輸出我們想要的結果”。然而,在java中面向對象編程強調“必須通過對象的形式來做事情”,如此函數式編程與面向對象編程便區別了出來,前者強調“做什麼”,後者強調“以什麼形式做”。就好像有人說去日本,函數式編程就說一句去日本,而面向對象編程便要考慮是坐飛機去日本還是開船去日本。
lanmbda表達式就是函數式編程的一種體現,它強調的是做什麼,而不講究以什麼形式去做。使得編程的關注點聚焦於方法。
lambda表達式的形式
在Java8以前的java代碼中,經常使用內部匿名類的形式作爲接口實現類對象參數
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任務B執行了...");
}
}).start();
這樣的代碼看起來很繁瑣,如果使用了Lambda後
new Thread(
() -> {
System.out.println("任務C執行了...");
}
).start();
從代碼結構中就可以看出規律,lanmbda表達式的書寫格式就是:
只需要按照匿名內部類的形式寫出代碼,然後保留抽象方法的()和{},在()和{}之間添加一個->
因此lambda表達式的組成
就是:() -> {},其中()中類型可以省略,要是隻有一個參數()也可以省略;{}中如果只有一行語句,那麼return和末尾分號都可以省略;->是絕對不能省略的。
lambda表達式的使用前提
- 使用lambda代替的是函數式接口抽象方法的實現,也就是要求接口中有且僅有一個抽象方法。
- 必須要有方法使用接口作爲參數。也就是方法的參數或局部變量類型必須爲Lambda對應的接口類型,才能使用Lambda作爲該接口的實例。
Java 8中專門爲函數式接口引入了一個新的註解: @FunctionalInterface ,專門用來檢測是不是函數式接口
(重點!)常用函數式接口
1、Comparator:比較型接口,用於兩個對象的比較,其中抽象方法compare
接受兩個參數o1,o2,如果return o1 - o2表示升序,如果return o2 - o1表示降序(o1和o2都不是引用類型對象)
Person[] arr = new Person[]{
new Person("ttt",12),
new Person("qw",24),
new Person("rr",38),
new Person("cc",45),
new Person("uu",38),
};
// 匿名類寫法
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
// lambda標準寫法
Arrays.sort(arr, (Person o1, Person o2) -> {
return o2.getAge() - o1.getAge();
});
// lambda簡化寫法
Arrays.sort(arr,(o1, o2) -> o1.getAge() - o2.getAge());
2、Conusmer:消費型接口,給他的抽象方法accept
一個參數,就叫消費一個對象。
public static void main(String[] args) {
String str = "Hello World";
// 給一個str對象消費掉
// 匿名內部類的形式
method(str, new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// lanmbda標準形式
method(str,(String s)->{
System.out.println("長度:" + s.length());
});
// lambda簡化形式
method(str,s -> System.out.println("全部大寫" + s.toUpperCase()));
}
public static void method(String str,Consumer<String> consumer) {
// 調用抽象方法
consumer.accept(str);
}
3、Predicate:判斷型接口,返回一個boolean類型作爲判斷結果。
public static void main(String[] args) {
String str = "Helloworld";
// 匿名內部類對象的方式
method(str, new Predicate<String>() {
@Override
public boolean test(String s) {
//判斷字符串長度是否大於5
return s.length()>5;
}
});
// lambda表達式的標準方式
method(str,(String s)->{
return s.length()>5;
});
// lambda表達式的簡化形式
method(str,s->s.length()>5);
}
/**
* 定義方法,使用函數式接口Predicate作爲參數
*/
public static void method(String str,Predicate<String> p){
boolean result = p.test(str);
System.out.println(result);
}
Stream流
Java8以後,引入了Stream,此流非彼流,它本質上強調的是工作流程,用於解決集合類中既有的弊端,比如在遍歷集合的同時要做一些篩選過濾。
一、展示什麼叫優雅
Stream綜合練習
- 第一個隊伍只要名字爲3個字的成員姓名;
- 第一個隊伍篩選之後只要前3個人;
- 第二個隊伍只要姓張的成員姓名;
- 第二個隊伍篩選之後不要前2個人;
- 將兩個隊伍合併爲一個隊伍;
- 打印整個隊伍的姓名信息。
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪麗熱巴");
one.add("宋遠橋");
one.add("蘇星河");
one.add("老子");
one.add("莊子");
one.add("孫子");
one.add("洪七公");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("張無忌");
two.add("張三丰");
two.add("趙麗穎");
two.add("張二狗");
two.add("張天愛");
two.add("張三");
Stream.concat(one.stream().filter(s -> s.length() == 3).limit(3)
,two.stream().filter(s -> s.startsWith("張")).skip(2))
.forEach(s-> System.out.println(s));
}
三行代碼完成上面的需求!過程清晰明瞭!
二、獲取Stream對象
1、通過Collection 集合
public default Stream<E> stream() 方法獲取,因此Collection的實現類都能獲取流對象。
- 注意:Map集合的實現類要通過Stream方式操作的話只能先獲取key集合、value集合或者entry集合,通過Set集合來進行操作。
2、Stream接口的靜態方法
static <T> Stream<T> of(T… values) ,可以傳入數組來獲取流對象。
三、Stream常用方法
1、filter方法
Stream <T> filter(Predicate predicate) : 按照方法參數predicate對Stream流對象中的元素進行過濾,並返回新的Stream流對象
2、limit方法和skip方法
Stream<T> limit(long n): 獲取流對象中的前n個元素,返回新的Stream流對象
Stream<T> skip(long n): 跳過流對象中的前n個元素,返回新的Stream流對象
3、contact方法
static <T> Stream<T> concat(Stream<T> a, Stream<T> b): ,把兩個流對象a和b合併成一個流對象並返回新的流對象
4、count方法
long count() : 終結方法,調用此方法後,Stream流對象將不可以繼續使用
5、forEach方法
void forEach(Consumer<T> action): 終結方法,調用此方法後,Stream流對象將不可以繼續使用
該方法接收一個Consumer接口函數
,會將每一個流元素交給該函數進行處理,不保證順序.
使用過程可能拋出的異常
Exception in thread “main” java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at com.lambda.DemoFilter.main(DemoFilter.java:16)
這是重複使用了已經用過的Stream的對象而導致的問題,因爲每個Stream的對象只能用一次。
總結:
1、終結方法:count、forEach,返回值都不是Stream對象,不能再鏈式調用
2、流對象只能被使用一次
總結
Lambda表達式簡化了Java編程的複雜性,爲函數式接口編程提供了一個面向函數編程的方式,不用再書寫繁瑣的匿名內部類。