測試示例源碼 https://gitee.com/luffy_code/java8.gitjava
Lambda 表達式,它是推進 Java 8 發佈的最重要新特性。 Lambda 容許把函數做爲一個方法的參數(函數做爲參數傳遞進方法中)。 Lambda 表達式可使代碼變的更加簡潔緊湊 Lambda 是一個匿名函數,咱們能夠把 Lambda 表達式理解爲是一段能夠傳遞的代碼(將代碼 像數據同樣進行傳遞)。能夠寫出更簡潔、更 靈活的代碼。做爲一種更緊湊的代碼風格,使 Java的語言表達能力獲得了提高。mysql
Lambda 表達式語法 Lambda 表達式在Java 語言中引入了一個新的語法元 素和操做符。這個操做符爲 「->」 , 該操做符被稱 爲 Lambda 操做符或剪頭操做符。它將 Lambda 分爲 兩個部分: 左側:指定了 Lambda 表達式須要的全部參數 右側:指定了 Lambda 體,即 Lambda 表達式要執行 的功能。 如下是lambda表達式的重要特徵: **可選類型聲明:**不須要聲明參數類型,編譯器能夠統一識別參數值。 **可選的參數圓括號:**一個參數無需定義圓括號,但多個參數須要定義圓括號。 **可選的大括號:**若是主體包含了一個語句,就不須要使用大括號。 **可選的返回關鍵字:**若是主體只有一個表達式返回值則編譯器會自動返回值,大括號須要指定明表達式返回了一個數值。git
什麼是函數式接口 只包含一個抽象方法的接口,稱爲函數式接口。 你能夠經過 Lambda 表達式來建立該接口的對象。(若 Lambda 表達式拋出一個受檢異常,那麼該異常須要在目標接口的抽象方 法上進行聲明)。 咱們能夠在任意函數式接口上使用 @FunctionalInterface 註解, 這樣作能夠檢查它是不是一個函數式接口,同時 javadoc 也會包 含一條聲明,說明這個接口是一個函數式接口。sql
Java 內置四核心函數式接口數組
函數式接口 | 參數類型 | 返回類型 | 用途 | |
Consumer<T> 消費型接口 | T | void | 對類型爲T的對象應用操 做,包含方法: void accept(T t) | |
Supplier<t> 供給型接口 | 無 | T | 返回類型爲T的對象,包 含方法:T get(); | |
Function<T,R> 函數型接口 | T | R | 對類型爲T的對象應用操 做,並返回結果。結果 是R類型的對象。包含方 法:R apply(T t); | |
Predicate<T> 判定型接口 | T | boolean | 肯定類型爲T的對象是否 知足某約束,並返回 boolean 值。包含方法 boolean test(T t); | |
... | ... | ... | 基於四核心函數式接口 衍生了不少子接口 |
/** * @ClassName: Lambda * @Description: lambda表達式 * @author: <a href="liuyafengwy@163.com">luffy</a> * @date: */ public class Lambda { /* 1、Lambda 表達式的基礎語法 "->" 該操做符稱爲箭頭操做符或 Lambda 操做符 箭頭操做符將 Lambda 表達式拆分紅兩部分: 左側:Lambda 表達式的參數列表 右側:Lambda 表達式中所需執行的功能, 即 Lambda 體 2、Lambda 表達式須要「函數式接口」的支持 函數式接口:接口中只有一個抽象方法的接口,稱爲函數式接口。 可使用註解 @FunctionalInterface 修飾 能夠檢查是不是函數式接口 上聯:左右遇一括號省 下聯:左側推斷類型省 橫批:能省則省 */ @Test public void test1(){ //在1.8以前如局部內部類引用了, 局部變量 該變量必須是final修飾, 但1.8以後默認加了final不須要顯示申明,實際仍是final int num = 0; //------------------------------------------------------------------------------------------------------------------------------------ //原寫法 Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello World!" + num); } }; r.run(); //------------------------------------------------------------------------------------------------------------------------------------ //使用lambda方式, 更加簡潔 注意源碼中Runnable類被@FunctionalInterface 爲***函數式接口** Runnable r1 = () -> System.out.println("Hello Lambda!"); r1.run(); //------------------------------------------------------------------------------------------------------------------------------------ } List<Employee> employeeList = CollectionUtil.newArrayList( new Employee(1L, "張三", 18, Employee.Gender.MAN, 9999.99, Employee.Department.DEVELOP), new Employee(2L, "李四", 30, Employee.Gender.MAN, 5555.55, Employee.Department.DEVELOP), new Employee(3L, "王五", 20, Employee.Gender.MAN, 3333.33, Employee.Department.DEVELOP), new Employee(4L, "趙六", 21, Employee.Gender.MAN, 7777.77, Employee.Department.DEVELOP), new Employee(5L, "鐵錘", 21, Employee.Gender.WOMAN, 7777.77, Employee.Department.DESIGN) ); //===================================================================================================================================================================================================================================================== /** * 消費型接口 Consumer<T> * 傳入對象類型 : T * 返回對象類型 : void (無返回值) * 對類型爲T的對象應用操做,無返回值, 包含方法: void accept(T t) */ @Test public void testConsumer(){ //------------------------------------------------------------------------------------------------------------------------------------ // 1.8以前,寫法實現接口中方法 Consumer<String> consumerOld = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; consumerOld.accept("Hello 消費性函數接口, 有一個參數無返回值, 將傳入參數進行消費(操做)"); // 1.8以後 寫法使用lambda,簡潔語法 Lambda 表達式的參數列表的數據類型能夠省略不寫,由於JVM編譯器經過上下文推斷出,數據類型,即「類型推斷」 Consumer<String> consumerNew = (str) -> System.out.println(str); consumerNew.accept("Hello 消費性函數接口, 有一個參數無返回值, 將傳入參數進行消費(操做)"); //------------------------------------------------------------------------------------------------------------------------------------ //排序 - 將list集合中對象按年齡排序 Collections.sort(employeeList, (emp1, emp2) -> emp1.getAge().compareTo(emp2.getAge())); System.out.println(employeeList); //[Employee(id=1, name=張三, age=18, gender=MAN, salary=9999.99, department=DEVELOP), Employee(id=3, name=王五, age=20, gender=MAN, salary=3333.33, department=DEVELOP), Employee(id=4, name=趙六, age=21, gender=MAN, salary=7777.77, department=DEVELOP), Employee(id=5, name=鐵錘, age=21, gender=WOMAN, salary=7777.77, department=DESIGN), Employee(id=2, name=李四, age=30, gender=MAN, salary=5555.55, department=DEVELOP)] //------------------------------------------------------------------------------------------------------------------------------------ //爲每位員工加薪 1 元 Consumer<List<Employee>> consumer = (empList) -> { for (Employee employee : empList) { Double salary = NumberUtil.add(employee.getSalary().doubleValue(), 1d); employee.setSalary(salary); } }; consumer.accept(employeeList); System.out.println(employeeList); // [Employee(id=1, name=張三, age=18, gender=MAN, salary=10000.99, department=DEVELOP), Employee(id=3, name=王五, age=20, gender=MAN, salary=3334.33, department=DEVELOP), Employee(id=4, name=趙六, age=21, gender=MAN, salary=7778.77, department=DEVELOP), Employee(id=5, name=鐵錘, age=21, gender=WOMAN, salary=7778.77, department=DESIGN), Employee(id=2, name=李四, age=30, gender=MAN, salary=5556.55, department=DEVELOP)] //將員工的薪水提取出來, 並打印 employeeList.stream() .map((Function<Employee, Double>) Employee::getSalary) .forEach((x) -> System.out.print(" $"+x)); //$10000.99 $3334.33 $7778.77 $7778.77 $5556.55 //------------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------------ } //===================================================================================================================================================================================================================================================== /** * 供給型接口 Supplier<t> * 傳入對象類型 : 無 * 返回對象類型 : T * 無需參數,返回類型爲T的對象,包 含方法:T get(); */ @Test public void testSupplier(){ //------------------------------------------------------------------------------------------------------------------------------------ //返回員工總數, 只因測試該函數接口原理,才這樣去寫, 無需參數,並返回制定類型 Supplier<Integer> supplier = () -> employeeList.size(); Integer size = supplier.get(); System.out.println("員工總數爲:"+size); //------------------------------------------------------------------------------------------------------------------------------------ } //===================================================================================================================================================================================================================================================== /** * 函數型接口 :Function<T,R> * 傳入對象類型 : T * 返回對象類型 : R * 對類型爲T的對象應用操 做,並返回結果。結果 是R類型的對象。包含方 法:R apply(T t); */ @Test public void testFunction(){ //------------------------------------------------------------------------------------------------------------------------------------ //傳入員工集合, 將員工薪水相加並返回總數 Function<List<Employee>,Double> function = (empList) -> { Double sumSalary = 0d; for (Employee employee : empList) { Double salary = employee.getSalary(); sumSalary = NumberUtil.add(sumSalary, salary); } return sumSalary; }; Double sum = function.apply(employeeList); System.out.println(sum); //------------------------------------------------------------------------------------------------------------------------------------ } //===================================================================================================================================================================================================================================================== /** * 斷言型接口: Predicate<T> * 傳入對象類型 : T * 傳入對象類型 : boolean * 斷定類型爲T的對象是否 知足某約束,並返回 boolean 值。包含方法 boolean test(T t); */ @Test public void testPredicate(){ //------------------------------------------------------------------------------------------------------------------------------------ //調用本地方法得到年齡大於18的員工姓名集合 List<String> names = this.queryEmpName(employeeList,(e) ->{ return e.getAge() >= 18; }); System.out.println(names); //------------------------------------------------------------------------------------------------------------------------------------ } /** * 獲取員工名稱集合,按照傳入的Lambda表達式的判斷規則 * @param emplist 員工集合 * @param predicate 判斷規則 * @return 員工名稱集合 */ public List<String> queryEmpName(List<Employee> emplist, Predicate<Employee> predicate){ List<String> nameStr = CollectionUtil.newArrayList(); for (Employee employee : emplist) { if(predicate.test(employee)){ //調用 predicate.test 會執行方法調用者參數傳入的lambda表達式 nameStr.add(employee.getName()); } } return nameStr; } //===================================================================================================================================================================================================================================================== }
1、方法引用:若 Lambda 體中的功能,已經有方法提供了實現,可使用方法引用 *微信
(能夠將方法引用理解爲 Lambda 表達式的另一種表現形式)多線程
三種語法方式app
注意:框架
2、構造器引用 :構造器的參數列表,須要與函數式接口中參數列表保持一致!ide
1. 類名 :: new * 3、數組引用 * 類型[] :: new;
@Test public void testMethodRef(){ /** * 對象的引用 :: 實例方法名 * System.out 獲得對象 java.io.PrintStream, 對象中有一個方法爲 public void println(Object x) 的方法, * 使用消費性函數接口Consumer 方法引用所引用的方法的參數列表與返回值類型,須要與函數式接口中抽象方法的參數列表和返回值類型保持一致! * Consumer中accept方法和 PrintStream中println方法 參數列表與返回值類型形同 都是一個參數 而且返回值類型都是void * 符合 對象的引用 :: 實例方法名 使用該語法 */ PrintStream printStream = System.out; //原方式 使用lambda Consumer<List<Employee>> consumer1 = (x) -> printStream.println(x); consumer1.accept(employeeList); //[Employee(id=1, name=張三, age=18, gender=MAN, salary=9999.99, department=DEVELOP), Employee(id=2, name=李四, age=30, gender=MAN, salary=5555.55, department=DEVELOP), Employee(id=3, name=司馬韭菜, age=20, gender=MAN, salary=3333.33, department=DEVELOP), Employee(id=4, name=諸葛山珍, age=88, gender=MAN, salary=7777.77, department=DEVELOP), Employee(id=4, name=王鋼蛋, age=16, gender=MAN, salary=7777.77, department=DEVELOP), Employee(id=5, name=於鐵錘, age=16, gender=WOMAN, salary=7777.77, department=DESIGN)] //符合 對象的引用 :: 實例方法名 使用該語法 Consumer<List<Employee>> consumer2 = printStream::println; consumer2.accept(employeeList); //[Employee(id=1, name=張三, age=18, gender=MAN, salary=9999.99, department=DEVELOP), Employee(id=2, name=李四, age=30, gender=MAN, salary=5555.55, department=DEVELOP), Employee(id=3, name=司馬韭菜, age=20, gender=MAN, salary=3333.33, department=DEVELOP), Employee(id=4, name=諸葛山珍, age=88, gender=MAN, salary=7777.77, department=DEVELOP), Employee(id=4, name=王鋼蛋, age=16, gender=MAN, salary=7777.77, department=DEVELOP), Employee(id=5, name=於鐵錘, age=16, gender=WOMAN, salary=7777.77, department=DESIGN)] /** * 類名 :: 靜態方法名 * 函數式接口java.util.function.Supplier 中 int get();方法 * 類 java.time.LocalDate 中 public static LocalDate now() 靜態方法參數列表個數形同(都無參), 與返回值類型符合, 使用方法引用語法 類名 :: 靜態方法名 */ //原方式 使用lambda Supplier<LocalDate> supplier1 = () -> LocalDate.now(); LocalDate localDate1 = supplier1.get(); System.out.println(localDate1); //符合 類名 :: 靜態方法名 使用方法引用方式實現Comparator方法,並重寫compare 方法 Supplier<LocalDate> supplier2 = LocalDate::now; LocalDate localDate12 = supplier2.get(); System.out.println(localDate12); /** * 類名 :: 實例方法名 * ②若Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: ClassName::MethodName */ //原方式 使用lambda BiFunction<String,String,String> biFunction1 = (x,y) -> x.concat(y); String str1 = biFunction1.apply("hello", "word"); System.out.println(str1); //符合 類名 :: 靜態方法名 而且Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: ClassName::MethodName BiFunction<String,String,String> biFunction2 = String::concat; String str2 = biFunction2.apply("hello", "word"); System.out.println(str2); /** * 2、構造器引用 :構造器的參數列表,須要與函數式接口中參數列表保持一致! * 1. 類名 :: new */ //對象建立 符合 類名 :: new Supplier<Employee> emp = Employee::new; Employee employee = emp.get(); //調用get方法建立出Employee對象 //原方式 使用lambda數組建立 Function<Integer, String[]> fun = (args) -> new String[args]; String[] strs1 = fun.apply(10); System.out.println(strs1.length); //符合 類名 :: new Function<Integer, String[]> fun2 = String[] :: new; String[] strs2 = fun2.apply(10); System.out.println(strs2.length); }
Stream 是 Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對 集合進行的操做,能夠執行很是複雜的查找、過濾和映射數據等操做。 使用Stream API 對集合數據進行操做。也可使用 Stream API 來並行執行操做。簡而言之, Stream API 提供了一種高效且易於使用的處理數據的方式。
注意:
/** * @ClassName: StreamaAPI * @Description: StreamaAPI * @author: <a href="liuyafengwy@163.com">luffy</a> * @date: 2020/1/13 14:48 */ public class StreamaAPI { private List<Employee> employeeList; { Employee emp1 = Employee.builder() .id(1L) //主鍵 .name("張三") //姓名 .age(30) //年齡 .gender(Employee.Gender.WOMAN) //性別 .salary(5555.55) //薪資 .department(Employee.Department.SALES) //部門 .project(new Project("微信", "1KW", LocalDate.of(2019, 11, 2))) //當前項目 .historyProject(CollectionUtil.newArrayList( //歷史項目集合 new Project("搜狗拼音", "1KW", LocalDate.of(2019, 11, 2)), new Project("QQ輸入", "1KW", LocalDate.of(2019, 11, 2)), new Project("愛奇藝", "1KW", LocalDate.of(2019, 11, 2)) )).build(); Employee emp2 = Employee.builder() .id(2L) .name("李四") .age(40) .gender(Employee.Gender.MAN) .salary(5555.55) .department(Employee.Department.DEVELOP) .project(new Project("歡樂鬥地主", "1KW", LocalDate.of(2019, 11, 2))) .historyProject(CollectionUtil.newArrayList( new Project("搜狗拼音", "1KW", LocalDate.of(2019, 11, 2)), new Project("TM", "1KW", LocalDate.of(2019, 11, 2)) )).build(); Employee emp3 = Employee.builder() .id(3L) .name("司馬韭菜") .age(20) .gender(Employee.Gender.MAN) .salary(3333d) .department(Employee.Department.DEVELOP) .project(new Project("微信", "1KW", LocalDate.of(2019, 11, 2))) .historyProject(CollectionUtil.newArrayList( new Project("QQ", "1KW", LocalDate.of(2019, 11, 2)), new Project("TM", "1KW", LocalDate.of(2019, 11, 2)), new Project("飛秋", "1KW", LocalDate.of(2019, 11, 2)) )).build(); Employee emp4 = Employee.builder() .id(4L) .name("諸葛山珍") .age(20) .gender(Employee.Gender.WOMAN) .salary(3333d) .department(Employee.Department.DEVELOP) .project(new Project("微信", "1KW", LocalDate.of(2019, 11, 2))) .historyProject(CollectionUtil.newArrayList( new Project("金山詞霸", "1KW", LocalDate.of(2019, 11, 2)), new Project("有道詞典", "1KW", LocalDate.of(2019, 11, 2)) )).build(); Employee emp5 = Employee.builder() .id(5L) .name("王鋼蛋") .age(16) .gender(Employee.Gender.MAN) .salary(7777.77) .department(Employee.Department.DEVELOP) .project(null) .historyProject(CollectionUtil.newArrayList( new Project("騰訊視頻", "1KW", LocalDate.of(2019, 11, 2)), new Project("愛奇藝", "1KW", LocalDate.of(2019, 11, 2)) )).build(); Employee emp6 = Employee.builder() .id(6L) .name("於鐵錘") .age(68) .gender(Employee.Gender.WOMAN) .salary(7777.77) .department(Employee.Department.DESIGN) .project(new Project("微信", "1KW", LocalDate.of(2019, 11, 2))) .historyProject(CollectionUtil.newArrayList()).build(); employeeList = CollectionUtil.newArrayList(emp1,emp2,emp3,emp4,emp5,emp6); } //1. 建立 Stream 的幾種方式 @Test public void testCreate(){ /** * 1.Collection 提供了兩個方法獲取流 * stream() //串行流 * parallelStream() //並行流,多線程 */ List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //獲取一個順序流 Stream<String> parallelStream = list.parallelStream(); //獲取一個並行流 /** * 2. 經過 Arrays 中的 stream(T[] array) 獲取一個數組流 */ Integer[] nums = new Integer[10]; Stream<Integer> stream1 = Arrays.stream(nums); /** * 3. 經過 Stream 類中靜態方法 of(T... values) */ Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6); /** * 4. 建立無限流 一直在不停的產生 */ //迭代 iterate(final T seed, final UnaryOperator<T> f) 一直調用從第一個參數seed種子開始 Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2) .limit(10); //limit(long maxSize); 只獲取10, 相似於mysql 的limit stream3.forEach(System.out::println); //生成 一直調用generate中的lambda方法,獲取當前時間Long值, 使用limit限制了只取100條 Stream<Long> stream4 = Stream.generate(()-> new Date().getTime()) //Stream<T> generate(Supplier<T> s) 須要一個共計形接口 .limit(100); stream4.forEach(System.out::println); } /* 篩選與切片 filter——接收 Lambda , 從流中排除某些元素。 limit——截斷流,使其元素不超過給定數量。 skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補 distinct——去重,經過流所生成元素的 hashCode() 和 equals() 去除重複元素 */ @Test public void test2(){ Stream<Employee> empStream = employeeList.stream() .filter((e) -> { //filter中接斷言形接口, 返回true的數據進行了保留 return e.getGender().equals(Employee.Gender.MAN); } ) .skip(1) //跳過第一條 集合中的數據跳過指定條數數據,從下一條開始 .limit(3) // 值保留3條數據 .distinct(); //去掉經過流所生成元素的 hashCode() 和 equals() 去除重複元素 empStream.forEach(System.out::println); } /* 映射 map——接收 Lambda , 將元素轉換成其餘形式或提取信息。接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素。 flatMap——接收一個函數做爲參數,將流中的每一個值都換成另外一個流,而後把全部流鏈接成一個流 */ @Test public void test3(){ //---------------獲取員工歷史項目 ,並打印 ---------------------------------- Stream<Stream<Project>> mapStream1 = employeeList.stream() .map(e -> e.getHistoryProject().stream());//獲取員工歷史項目集合流 不使用方法引用 mapStream1.forEach((e) ->{ e.forEach((p) ->{ //不使用方法引用 System.out.println(p); }); }); //使用方法引用 Stream<Stream<Project>> mapStream2 = employeeList.stream() .map(StreamaAPI::getEmpHistroyProject); //獲取員工歷史項目集合流 使用方法引用 類名 :: 靜態方法 mapStream2.forEach(e -> e.forEach(System.out::println)); //使用方法引用 //--------------- 獲取員工歷史項目, 應爲使用了 flatMap 全部流鏈接成一個流 ---------------------------------- Stream<Project> flatMapStream1 = employeeList.stream() .flatMap(e -> e.getHistoryProject().stream());//獲取員工歷史項目集合流, 注意flatMap的特性全部流鏈接成一個流 不使用方法引用 flatMapStream1.forEach((p) -> System.out.println(p)); //使用方法引用 Stream<Project> flatMapStream2 = employeeList.stream() .flatMap(StreamaAPI::getEmpHistroyProject); //獲取員工歷史項目集合流, 注意flatMap的特性全部流鏈接成一個流 使用方法引用 類名 :: 靜態方法 flatMapStream2.forEach(System.out::println); } /** * 獲取 員工對象中的歷史項目並轉換成Stream流 * @param emp 員工對象 * @return 返回歷史項目流 */ private static Stream<Project> getEmpHistroyProject(Employee emp){ List<Project> historyProject = emp.getHistoryProject(); return historyProject.stream(); } /* sorted()——天然排序 按照對象實現Comparable接口 本身實現的compareTo方法排序, 爲對象的天然排序 sorted(Comparator com)——定製排序 本身重寫繼承Comparator,實現compareTo方法排序規則, 自定義排序 */ public void test4(){ //按照String對象實現的compareTo方法排序 employeeList.stream() .map(Employee::getName) .sorted() .forEach(System.out::println); //年齡不一樣按年齡排序, 年齡相同按姓名排序 employeeList.stream() .sorted((x, y) -> { if(x.getAge() == y.getAge()){ return x.getName().compareTo(y.getName()); }else{ return Integer.compare(x.getAge(), y.getAge()); } }).forEach(System.out::println); } /* allMatch——檢查是否匹配全部元素 anyMatch——檢查是否至少匹配一個元素 noneMatch——檢查是否沒有匹配的元素 findFirst——返回第一個元素 findAny——返回當前流中的任意元素 count——返回流中元素的總個數 max——返回流中最大值 min——返回流中最小值 */ @Test public void test5(){ //allMatch——檢查是否匹配全部元素 所有員工當前項目都是null 纔會true boolean b1 = employeeList.stream().allMatch((e) -> e.getProject() == null); System.out.println(b1); //anyMatch——檢查是否至少匹配一個元素 有一位員工當前項目是null 就是true boolean b2 = employeeList.stream().anyMatch((e) -> e.getProject() == null); System.out.println(b2); //noneMatch——檢查是否沒有匹配的元素 若是沒有部門爲DEVELOP 的返回true boolean b3 = employeeList.stream().noneMatch((e) -> e.getDepartment().equals(Employee.Department.DEVELOP)); System.out.println(b3); //findFirst——返回第一個元素 Optional<Employee> optional = employeeList.stream().findFirst(); //Optional.orElse(T other) 若是對象爲空,則返回orElse 的參數 Employee employee = optional.orElse(new Employee(-0L, "默認對象", 0, Employee.Gender.MAN, 9999.99, Employee.Department.DEVELOP)); System.out.println(employee); //findFirst——返回第一個元素 **使用了並行流 多個線程隨機獲取一個. 取到的值不必定是集合中哪個** 數據較大時候纔會有效果,否則總是固定一個 Optional<Employee> optional1 = employeeList.parallelStream().findAny(); //Optional.orElse(T other) 若是對象爲空,則返回orElse 的參數 Employee employee1 = optional1.orElse(new Employee(-0L, "默認對象", 0, Employee.Gender.MAN, 9999.99, Employee.Department.DEVELOP)); System.out.println(employee1); //count——返回流中元素的總個數 long count = employeeList.stream().count(); System.out.println(count); //max——返回流中最大值 Optional<Employee> optionalMax = employeeList.stream().max((e, e2) -> e.getSalary().compareTo(e2.getSalary())); //不使用方法引用 System.out.println("max——"+optionalMax.get()); Optional<Employee> optionalMax1 = employeeList.stream().max(Comparator.comparing(Employee::getSalary));//方法引用 System.out.println("max1——"+optionalMax1.get()); //min——返回流中最大值 Optional<Employee> optionalmin = employeeList.stream().min((e, e2) -> e.getSalary().compareTo(e2.getSalary())); //不使用方法引用 System.out.println("min——"+optionalmin.get()); Optional<Employee> optionalmin1 = employeeList.stream().min(Comparator.comparing(Employee::getSalary));//方法引用 System.out.println("min1——"+optionalmin1.get()); } /* 歸約 reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——能夠將流中元素反覆結合起來,獲得一個值。 */ @Test public void test6(){ //------------------------------------- List<Integer> list = Arrays.asList(1,2,3); Integer sum = list.stream() .reduce(0, (x, y) -> x + y); System.out.println(sum); //6 //------------------------------------ Optional<Double> reduce = employeeList.stream() .map(Employee::getSalary) //獲取員工的全部工資 .reduce(Double::sum); //將員工工資求和 System.out.println(reduce.get()); //工資總和 } /** * 收集器 * collect——將流轉換爲其餘形式。接收一個 Collector接口的實現,用於給Stream中元素作彙總的方法 */ @Test public void test7(){ //收集器用戶名到list Collectors.toList() List<String> list = employeeList.stream() .map(Employee::getName) .collect(Collectors.toList()); list.forEach(System.out::println); //收集器用戶名到set Collectors.toSet() Set<String> set = employeeList.stream() .map(Employee::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); //收集器用戶名到HashSet Collectors.toCollection(HashSet::new) HashSet<String> hs = employeeList.stream() .map(Employee::getName) .collect(Collectors.toCollection(HashSet::new)); hs.forEach(System.out::println); //獲取工資最大值 Optional<Double> max = employeeList.stream() .map(Employee::getSalary) .collect(Collectors.maxBy(Double::compare)); System.out.println(max.get()); //獲取工資最小值,按照比較器 Optional<Employee> op = employeeList.stream() .collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))); System.out.println(op.get()); //工資總和 Double sum = employeeList.stream() .collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sum); //工資平均值 Double avg = employeeList.stream() .collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); //總條目數 Long count = employeeList.stream() .collect(Collectors.counting()); System.out.println(count); //獲取集合薪資數據彙總信息對象, 返回對象中有個各個信息 DoubleSummaryStatistics dss = employeeList.stream() .collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(dss.getMax()); //最大 System.out.println(dss.getMin()); //最小 System.out.println(dss.getAverage()); //平均 System.out.println(dss.getCount()); //總條數 System.out.println(dss.getSum()); //總和 } //分組 @Test public void test8(){ //按部門信息進行分組 , map key是分組的部門枚舉, values是員工對象集合 Map<Employee.Department, List<Employee>> map = employeeList.stream() .collect(Collectors.groupingBy(Employee::getDepartment)); String s = JSONUtil.toJsonStr(map); System.out.println(s); /* { "DESIGN":Array[1], //設計組中一個對象 "SALES":Array[1], //銷售組中一個對象那個 "DEVELOP":Array[4] //開發組中4個對象 }*/ //多級分組 先按部門分, 再按年齡分 Map<Employee.Department, Map<String, List<Employee>>> map1 = employeeList.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.groupingBy((e) -> { if(e.getAge() >= 60) return "老年"; else if(e.getAge() >= 35) return "中年"; else return "成年"; }))); String s1 = JSONUtil.toJsonStr(map1); System.out.println(s1); /* { "DESIGN":{ "老年":Array[1] }, "SALES":{ "成年":Array[1] }, "DEVELOP":{ "成年":Array[3], "中年":Array[1] } }*/ //區分 知足條件的與不知足條件的進行區分 Map<Boolean, List<Employee>> map2 = employeeList.stream() .collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000)); String s2 = JSONUtil.toJsonStr(map2); System.out.println(s2); /* { "false":Array[2], // !(薪資>=5000) 有四名員工 "true":Array[4] // 薪資>= 5000 有兩名員工 } */ } }
經常使用方法介紹 Optional.of(T t) : 建立一個 Optional 實例 Optional.empty() : 建立一個空的 Optional 實例 Optional.ofNullable(T t):若 t 不爲 null,建立 Optional 實例,不然建立空實例 isPresent() : 判斷是否包含值 orElse(T t) : 若是調用對象包含值,返回該值,不然返回t orElseGet(Supplier s) :若是調用對象包含值,返回該值,不然返回 s 獲取的值 map(Function f): 若是有值對其處理,並返回處理後的Optional,不然返回 Optional.empty() flatMap(Function mapper):與 map 相似,要求返回值必須是Optional
並行流就是把一個內容分紅多個數據塊,並用不一樣的線程分 別處理每一個數據塊的流。 Java 8 中將並行進行了優化,咱們能夠很容易的對數據進行並 行操做。Stream API 能夠聲明性地經過 parallel() 與 sequential() 在並行流與順序流之間進行切換。
java8並行流底層使用Fork/Join 框架:就是在必要的狀況下,將一個大任務,進行拆分(fork)成若干個 小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 彙總. 瞭解 Fork/Join 框架 任務遞歸分配 成若干小任務 並行求值 fork fork fork fork join join join 部分結果進行合併
Fork/Join 框架與傳統線程池的區別 採用 「工做竊取」模式(work-stealing): 當執行新的任務時它能夠將其拆分分紅更小的任務執行,並將小任務加到線 程隊列中,而後再從一個隨機線程的隊列中偷一個並把它放在本身的隊列中。 相對於通常的線程池實現,fork/join框架的優點體如今對其中包含的任務的 處理方式上.在通常的線程池中,若是一個線程正在執行的任務因爲某些緣由 沒法繼續運行,那麼該線程會處於等待狀態.而在fork/join框架實現中,若是 某個子問題因爲等待另一個子問題的完成而沒法繼續運行.那麼處理該子 問題的線程會主動尋找其餘還沒有運行的子問題來執行.這種方式減小了線程 的等待時間,提升了性能.
Java 8中容許接口中包含具備具體實現的方法,該方法稱爲 「默認方法」,默認方法使用 default 關鍵字修飾。同時接口中容許添加靜態方法。
接口默認方法的」類優先」原則 若一個接口中定義了一個默認方法,而另一個父類或接口中又定義了一個同名的方法時選擇父類中的方法。若是一個父類提供了具體的實現,那麼 接口中具備相同名稱和參數的默認方法會被忽略。 接口衝突。若是一個父接口提供一個默認方法,而另外一個接 口也提供了一個具備相同名稱和參數列表的方法(無論方法 是不是默認方法),那麼必須覆蓋該方法來解決衝突!
LocalDate、LocalTime、LocalDateTime 類的實 例是不可變的對象,分別表示使用 ISO-8601日 歷系統的日期、時間、日期和時間。它們提供 了簡單的日期或時間,並不包含當前的時間信 息。也不包含與時區相關的信息。
用於「時間戳」的運算。它是以Unix元年(傳統 的設定爲UTC時區1970年1月1日午夜時分)開始 所經歷的描述進行運算
Duration:用於計算兩個「時間」間隔
Period:用於計算兩個「日期」間隔
TemporalAdjuster : 時間校訂器。有時咱們可能須要獲 取例如:將日期調整到「下個週日」等操做。
TemporalAdjusters : 該類經過靜態方法提供了大量的常 用 TemporalAdjuster 的實現。
java.time.format.DateTimeFormatter 類:該類提供了三種 格式化方法:
預約義的標準格式
語言環境相關的格式
自定義的格式
Java8 中加入了對時區的支持,帶時區的時間爲分別爲: ZonedDate、ZonedTime、ZonedDateTime 其中每一個時區都對應着 ID,地區ID都爲 「{區域}/{城市}」的格式 例如 :Asia/Shanghai 等 ZoneId:該類中包含了全部的時區信息 getAvailableZoneIds() : 能夠獲取全部時區時區信息 of(id) : 用指定的時區信息獲取 ZoneId 對象
/** * @ClassName: LocalDateTime * @Description: 時間 * @author: <a href="liuyafengwy@163.com">luffy</a> */ public class LocalDateTimeTest { /** * 1. LocalDate、LocalTime、LocalDateTime */ @Test public void tes1(){ //獲取當前時間 LocalDateTime now = LocalDateTime.now(); //指定時間 LocalDateTime ldt = LocalDateTime.of(2016, 11, 21, 10, 10, 10); System.out.println(ldt); //2016-11-21T10:10:10 System.out.println(ldt.getYear()); //2016 System.out.println(ldt.getMonthValue()); //11 System.out.println(ldt.getDayOfMonth());//21 System.out.println(ldt.getHour()); //10 System.out.println(ldt.getMinute()); //10 System.out.println(ldt.getSecond()); //10 ldt.toInstant(ZoneOffset.MAX); //加一年 ldt = ldt.plusYears(1); System.out.println(ldt); //2017-11-21T10:10:10 //減去2月 ldt = ldt.minusMonths(2); System.out.println(ldt); //2017-09-21T10:10:10 } /** * 2. Instant : 時間戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所經歷的毫秒值) */ @Test public void test2(){ //默認使用 UTC 時區 (差8小時) Instant ins = Instant.now(); System.out.println(ins); //時間偏移8小時 OffsetDateTime odt = Instant.now().atOffset(ZoneOffset.ofHours(8)); System.out.println(odt); //設置比1970-01-01T00:00:00 多1秒 Instant ins2 = Instant.ofEpochSecond(1); System.out.println(ins2); //1970-01-01T00:00:01Z } /** * Duration:用於計算兩個「時間」間隔 持續,持續的時間 * Period:用於計算兩個「日期」間隔 時期; (一段)時間 */ @Test public void test3(){ //指定時間 LocalDateTime ldt = LocalDateTime.of(2020,01,01,01,01,00); LocalDateTime ldt2 = LocalDateTime.of(2021,01,01,01,01,00); Duration between = Duration.between(ldt, ldt2); System.out.println("相差"+between.toString()); //相差PT8784H System.out.println("相差"+between.toMillis()+"毫秒"); //相差31622400000毫秒 System.out.println("相差"+between.getSeconds()+"秒"); //相差31622400秒 System.out.println("相差"+between.toMinutes()+"分鐘"); //相差527040分鐘 System.out.println("相差"+between.toHours()+"小時"); //相差8784小時 System.out.println("相差"+between.toDays()+"天"); //相差366天 LocalDate ld1 = LocalDate.of(2020, 01, 01); LocalDate ld2 = LocalDate.of(2021, 01, 01); Period pe = Period.between(ld1, ld2); System.out.println("相差="+pe); System.out.println("相差年="+pe.getYears()); //相差年=1 System.out.println("相差月="+pe.getMonths()); // 相差月=0 ** System.out.println("相差天="+pe.getDays()); //相差天=0 ** System.out.println("總計相差月="+pe.toTotalMonths()); //總計相差月=12 List<TemporalUnit> units = pe.getUnits(); System.out.println(units); // [Years, Months, Days] } //4. TemporalAdjuster : 時間校訂器 @Test public void test4(){ LocalDateTime ldt = LocalDateTime.of(2020,01,01,01,01,00); System.out.println(ldt); //2020-01-01T01:01 //下一個週日 LocalDateTime sunday = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); System.out.println(sunday); //2020-01-05T01:01 //設置日期 LocalDateTime ldt2 = ldt.withDayOfMonth(10); //設置日期偏移後2020-01-10T01:01 System.out.println("設置日期偏移後"+ldt2); //自定義:下一個工做日 LocalDateTime work = ldt.with((l) -> { LocalDateTime ldt4 = (LocalDateTime) l; //獲取當前周幾 DayOfWeek week = ldt4.getDayOfWeek(); if(week.equals(DayOfWeek.SUNDAY)){ //週五 return ldt4.plusDays(1); }else if(week.equals(DayOfWeek.SATURDAY)){ //週六 return ldt4.plusDays(2); }else{ return ldt4.plusDays(1); } }); System.out.println(work); } //5. DateTimeFormatter : 解析和格式化日期或時間 @Test public void test5(){ DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); //當前時間 LocalDateTime ldt = LocalDateTime.now(); //格式化當前時間爲字符串 String strDate = ldt.format(dtf); System.out.println(strDate); //2020年01月14日 17:45:55 //字符串格式化時間 LocalDateTime newLdt = ldt.parse(strDate, dtf); System.out.println(newLdt); //2020-01-14T17:45:55 } //6.ZonedDate、ZonedTime、ZonedDateTime : 帶時區的時間或日期 @Test public void test7(){ Set<String> set = ZoneId.getAvailableZoneIds(); set.forEach(System.out::println); LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); System.out.println(ldt); ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); System.out.println(zdt); } }