四大函數式接口
必須掌握的知識點
以前的程序員(知道jdk1.5的特性):
泛型、枚舉、反射和注解
新時代的程序員(因為jdk的版本都已經到13了):
所以要在這個三個基礎上,必須掌握4個:lambda表達式、鏈式編程、函數式接口、Stream流式計算
介紹
-
-
接口上面有
@FunctionalInterface(功能接口)的注解標注-
比如Runnable接口
-
@FunctionalInterface public interface Runnable { public abstract void run(); }
-
-
簡化編程模型,在新版本的框架底層大量應用!
-
foreach(消費者類的函數式接口)
-
四大函數式接口

Consumer函數式接口
使用代碼
package com.zxh.pool; import java.util.function.Consumer; // consumer:消費型接口:只有輸入,沒有輸出 public class FunctionInterfaceDemo { public static void main(String[] args) { // Consumer consumer = new Consumer<String>(){ // @Override // public void accept(String str){ // System.out.println(str); // } // }; // consumer.accept("123"); // 使用lambda表達式 Consumer<String> consumer = (str)->{System.out.println(str);}; consumer.accept("123"); } }

-
提供型接口:沒有輸入,只有輸出
-

使用代碼
package com.zxh.pool; import java.util.function.Supplier; /** * 1、Consumer:消費型接口:只有輸入,沒有輸出 * 2、Supplier:提供型接口:只有輸出,沒有輸入 */ public class FunctionInterfaceDemo { public static void main(String[] args) { // Supplier<Integer> supplier = new Supplier<Integer>() { // @Override // public Integer get() { // return 1024; // } // }; // 使用lambda表達式 Supplier<Integer> supplier = ()->{return 1024;}; System.out.println(supplier.get()); } }

Function函數式接口
-
可以有輸入,可以有輸出
-

使用代碼
package com.zxh.function; import java.util.function.Function; /** * 1、Consumer:消費型接口:只有輸入,沒有輸出 * 2、Supplier:提供型接口:只有輸出,沒有輸入 * 3、Function:有輸入,也有輸出 */ public class Demo03 { public static void main(String[] args) { // Function<String, String> function = new Function<String, String>() { // @Override // public String apply(String s) { // return s; // } // }; // lambda表達式 Function function = (str)->{return str;}; System.out.println(function.apply("123")); } }

Predicate函數式接口
-
斷定型接口
-
可以輸入,輸出boolean類型參數
-

使用測試
package com.zxh.function; import java.util.function.Predicate; /** * 1、Consumer:消費型接口:只有輸入,沒有輸出 * 2、Supplier:提供型接口:只有輸出,沒有輸入 * 3、Function:有輸入,也有輸出 * 4、Predicate:斷定型接口:有輸入,返回布爾型 */ public class Demo04 { public static void main(String[] args) { // Predicate<String> predicate = new Predicate<String>() { // @Override // public boolean test(String str) { // return str.isEmpty(); // } // }; // 使用lambda表達式 Predicate<String> predicate = (str)->{return str.isEmpty();}; System.out.println(predicate.test("123")); System.out.println(predicate.test("")); } }

在大數據中:會有存儲 + 計算
而儲存的過程就交給流操作!
jdk1.8文檔中有Stream接口

例題:包含所有新特性
1、有一個實體類User
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class User { private int id; private String name; private int age; }
2、題目要求
/** * 題目要求:一分鍾內完成此題,只能用一行代碼實現! * 現在有5個用戶!篩選: * 1、ID 必須是偶數 * 2、年齡必須大於23歲 * 3、用戶名轉為大寫字母 * 4、用戶名字母倒着排序 * 5、只輸出一個用戶! */ public class Test { public static void main(String[] args) { User u1 = new User(1,"a",21); User u2 = new User(2,"b",22); User u3 = new User(3,"c",23); User u4 = new User(4,"d",24); User u5 = new User(6,"e",25); // 集合就是存儲 List<User> users = Arrays.asList(u1, u2, u3, u4, u5); } }
Stream接口,涉及到的方法
-
filter():參數Predicate,就是段定型的函數式接口
-
該接口的參數可以進行判斷,並返回布爾型
-
就可以做到過濾、篩選
-

-
-
map():參數Function,就是只可以指定輸入輸出類型的函數式接口
-
輸入小寫字母:轉為大寫字母並返回。
-

-
-
sorted():參數Comparator,就是可以比較大小的函數式接口
-
該接口的參數可以傳入兩個相同的類型的參數,進行比較,並且返回int類型的值。
-
如果返回
-

-
Comparator函數式接口測試:
-
public class MyTest { public static void main(String[] args) { Comparator<Integer> comparator = (u1, u2)->{return u1 - u2;}; /* u1 - u2:u1和比u2是升序排列, 當 u1 - u2 < 0:也就是u1 < u2,不動 當 u1 - u2 > 0:也就是u1 > u2,換一下順序 u2 - u1:u2和比u1是降序排列, 當 u1 - u2 < 0:也就是u1 < u2,換一下順序 當 u1 - u2 > 0:也就是u1 > u2,不動 */ Arrays.asList(3, 1, 2).stream() .sorted((u1, u2) -> {return u1 - u2;}) .forEach(System.out::println); } }
-
-
limit():從上往下截取截取多少個參數
-
過濾:ID 必須是偶數
// stream流式計算,管理數據 // 應用的到了:lambda表達式、函數式接口、鏈式編程、Stream流式計算 users.stream() .filter((u)->{return u.getId()%2==0;}) // 過濾 ID 必須是偶數 .forEach(System.out::println);

過濾:年齡必須大於23歲
// stream流式計算,管理數據 // 應用的到了:lambda表達式、函數式接口、鏈式編程、Stream流式計算 users.stream() .filter((u)->{return u.getId()%2==0;}) // 過濾方法: ID 必須是偶數 .filter((u)->{return u.getAge() > 23;}) // 過濾方法: 年齡必須大於23歲 .forEach(System.out::println);

轉換:用戶名轉為大寫字母
users.stream() // lambda表達式:如果參數只有一個可以省略括號 .filter((u) -> {return u.getId()%2==0;}) // 過濾方法: ID 必須是偶數 .filter(u ->{return u.getAge() > 23;}) // 過濾方法: 年齡必須大於23歲 .map(u->{u.setName(u.getName().toUpperCase()); return u;}) // 轉換方法:用戶名轉為大寫字母 .forEach(System.out::println);

排序: 用戶名字母倒着排序
users.stream() // lambda表達式:如果參數只有一個可以省略括號 .filter((u) -> {return u.getId()%2==0;}) // 過濾方法: ID 必須是偶數 .filter(u ->{return u.getAge() > 23;}) // 過濾方法: 年齡必須大於23歲 .map(u->{u.setName(u.getName().toUpperCase()); return u;}) // 轉換方法:用戶名轉為大寫字母 .sorted((uu1, uu2)->{return uu2.getName().compareTo(uu1.getName());}) // 排序方法: 用戶名字母倒着排序 .forEach(System.out::println);

取指定個數:輸出一個用戶
users.stream() // lambda表達式:如果參數只有一個可以省略括號 .filter((u) -> {return u.getId()%2==0;}) // 過濾方法: ID 必須是偶數 .filter(u ->{return u.getAge() > 23;}) // 過濾方法: 年齡必須大於23歲 .map(u->{u.setName(u.getName().toUpperCase()); return u;}) // 轉換方法:用戶名轉為大寫字母 .sorted((uu1, uu2)->{return uu2.getName().compareTo(uu1.getName());}) // 排序方法: 用戶名字母倒着排序 .limit(1) // 取指定個數:輸出一個用戶 .forEach(System.out::println);

完成代碼
package com.zxh.Stream; import java.util.Arrays; import java.util.List; /** * 題目要求:一分鍾內完成此題,只能用一行代碼實現! * 現在有5個用戶!篩選: * 1、ID 必須是偶數 * 2、年齡必須大於23歲 * 3、用戶名轉為大寫字母 * 4、用戶名字母倒着排序 * 5、只輸出一個用戶! */ public class Test { public static void main(String[] args) { User u1 = new User(1,"a",21); User u2 = new User(2,"b",22); User u3 = new User(3,"c",23); User u4 = new User(4,"d",24); User u5 = new User(6,"e",25); // 集合就是存儲 List<User> users = Arrays.asList(u1, u2, u3, u4, u5); // stream流式計算,管理數據 // 應用的到了:lambda表達式、函數式接口、鏈式編程、Stream流式計算 users.stream() // lambda表達式:如果參數只有一個可以省略括號 .filter((u) -> {return u.getId()%2==0;}) // 過濾方法: ID 必須是偶數 .filter(u ->{return u.getAge() > 23;}) // 過濾方法: 年齡必須大於23歲 .map(u->{u.setName(u.getName().toUpperCase()); return u;}) // 轉換方法:用戶名轉為大寫字母 .sorted((uu1, uu2)->{return uu2.getName().compareTo(uu1.getName());}) // 排序方法: 用戶名字母倒着排序 .limit(1) // 取指定個數:輸出一個用戶 .forEach(System.out::println); } }
ForkJoin任務拆分
ForkJoin是什么
ForkJoin在JDK1.7的時候就已經出來了,用於並行執行任務!提高效率
具體比如大數據的Map Reduce:將一個大任務拆分成多個小任務執行
概念圖

ForkJoin特點
特點:工作竊取
這個里面維護的都是雙端隊列
概念圖
-
AB兩個線程,B線程執行完了會把A線程沒有執行完的任務偷過來了執行,增加效率
-
但是會產生A和B線程爭奪這個任務的情況,但還是利大於弊的

ForkJoin如何使用
肯定需要知道ForkJoin是怎么創建運行的?
1、點開JUC工具包

2、可以發現關於ForkJoinPool的兩個接口
-
它和線程池一樣,線程通過線程池創建,而
ForkJoin通過ForkJoinPool創建並執行的。 -
點開可以看到這兩個接口,對應只有一個相同的實現類
ForkJoinPool -
3、查看到實現類中有兩個關於ForkJoin的類

4、點開ForkJoinPool可以看到有說明
-
首先是實現的兩個接口
-

-
隨便點開一個可以看到,線程池ThreadPoolExecutor和ForkJoinPool是同一級的。
-

-
-
ForkJoinPool是運行ForkJoinTask的
-
5、怎么運行呢?ForkJoinPool對應提供了3個方法,具體如下:
-
ForkJoinPool有3個主要的執行方法
-
只執行任務,沒有返回值
-
-
執行任務,並返回結果
-
6、再查看ForkJoinTask這個類是什么?
-
有兩個實現類
-

7、這里我們使用RecursiveTask有返回值的實現類
-
怎么使用呢?文檔中有案例,直接繼承這個類就可以了
-

-
查看源碼,可以發現需要重寫一個抽象方法,該方法就是計算的
-

我們了解如何創建並使用ForkJoin,那我們接下來舉個例子,從1到10億進行累加,使用3中方法累加,查看最后的運行效率
舉例測試
package com.zxh.forkjoin; public class Test { public static void main(String[] args) { test1(); } // 普通方法 public static void test1(){ Long start = System.currentTimeMillis(); Long sum = 0L; // 使用JDK1.7的分隔符,可以講10億寫為:10_0000_0000 for (Long i = 1L; i <= 10_0000_0000; i++) { sum += i; } Long end = System.currentTimeMillis(); System.out.println("sum = "+ sum + " 運行時間:" + (end - start)); } }

使用ForkJoin優化方法
package com.zxh.forkjoin; import java.util.concurrent.RecursiveTask; /** * 這里模擬求和運算從start開始到end一直累加 * 如何使用ForkJoin? * 1、通過forkJoinPool 執行 * 2、執行計算任務 forkjoinPool.execute(ForkJoinTask task) * 3、計算類要繼承 RecursiveTask,因為他有返回值 */ public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; // 初始值 private Long end; // 最終值 // 臨界值,用於判斷這個任務的大小,如果超出這個數,就對半分割任務執行,提高效率 private Long temp = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; this.end = end; } // 計算方法,就相當於遞歸的操作 @Override protected Long compute() { // 具體任務的執行,並返回執行后的值 if((end - start) < temp){ // 判斷要計算的數字個數是否小邊界值 Long sum = 0L; // 使用JDK1.7的分隔符,可以講10億寫為:10_0000_0000 for (Long i = start; i <= end; i++) { sum += i; } return sum; // 返回計算結果 }else{ Long middle = (start + end) / 2; // 取中間數,將任務分割 ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork(); // 將拆分后的任務,壓入線程隊列 ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end); task2.fork(); // 將拆分后的任務,壓入線程隊列 return task1.join() + task2.join(); // 獲取結果,並返回計算結果 } } }
測試類
package com.zxh.forkjoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { test2(); //3993 } // ForkJoin優化方法 public static void test2() throws ExecutionException, InterruptedException { Long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L); // 計算任務 ForkJoinTask<Long> submit = forkJoinPool.submit(task); // 提交任務 Long sum = submit.get(); Long end = System.currentTimeMillis(); System.out.println("sum = "+ sum + " 運行時間:" + (end - start)); } }

使用Stream並行流

package com.zxh.forkjoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { test3(); //212 } // Stream流並行執行方法 public static void test3(){ Long start = System.currentTimeMillis(); // LongStream,並行流 // rangeClosed():(]最開右閉,設置計算數字從1到10_0000_0000 // parallel():開啟並行計算 // reduce():求和方式,並返回結果 long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum); Long end = System.currentTimeMillis(); System.out.println("sum = "+ sum + " 運行時間:" + (end - start)); } }

小結
有3 6 9的說法,說是工資3000 6000 9000,上面的方法對應着工資,如果想拿到高工資,使用Stream流是最好的了,至於底層還需要研究,掌握這么一點是不行的。








