# Java 8 新特性 ## Optional (java.util.Optional) > 處理NullPointerException議題 ## 1. Lambda expression (args) -> body 可以使用函數式程式的結構,適用於將**動作作為參數**給method的情況 - lambda表達是對象, 依賴函數interface, 變成函數式的表達;是函數式接口的instance就能用lambda處理,只處理這個method",因此語法概念就是"省略不必要的字": 1. Class 2. method 3. 變數型態 - 匿名函數 - 一段可以傳遞的程式: Lambda 在執行時不會 new 出一個物件實體,而是直接將 Lambda 的 body 程式碼存放在記憶體後直接呼叫,不像原本匿名類都需new instance, 多觸發gc回收降低效能 - 簡潔程式寫法 - 表達式3部分: (args) + 箭頭操作→ + 方法body - 參數形式 - `()`: 沒有引數 - `(X x)`: 一個 X 型別的引數 - `(x)`: 一個引數,省略型別 - `(X x, Y y)`: 兩個引數,分別為 X 和 Y 型別 - `(x, y)`: 兩個引數,省略型別 - 語法格式 - 沒有參數, 沒有返回值 - 一個參數, 沒有返回值(參數類型可省略, 由編譯氣自行”類型推斷”; 小括號可以省略) - 多個參數, 多個執行語句, 有返回值 - 只有一個執行語句, return跟大括號都可以省略 ```java public class Lambda { @Test // 無參數 public void test1NoArgs() { Runnable r1 = new Runnable() { @Override public void run() { System.out.println("non lambda"); } }; r1.run(); Runnable r2 = () -> System.out.println("lambda"); r2.run(); } @Test // 有參數 public void tes2Args() { Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; System.out.println("com1 non lambda" + com1.compare(1, 10)); Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2); System.out.println("com2 lambda" + com2.compare(1, 10)); // 方法引用 Comparator<Integer> com3 = Integer:: compare; System.out.println("com3 lambda" + com3.compare(1, 10)); } } ``` ## 2. Funtional interface - 只有一個抽象方法 (所以lambda才可以這樣省略,只要保留不同的部分即可) - 可以用lambda創建此interface的對象 - @FunctionalInterface註解可以省略, 不過加了能多加檢查是否為函數式接口 - 四大核心函數式interface 1. **Comsumer<T> void**: void accept(T t) 消費型, 消費T , 沒有返回值的情況 2. **Supplier “” T**: T get() 供給型, 返回T, 沒有給值 3. **Function<T, R>**: T R: R apply(T t) 函數型, 消費T, 返回R 4. **Predicate<T> boolean**: boolean test(T t) 判斷型, 判斷T是否符合特定條件 5. BiFunction<T,U,R>: apply() 消費T U, 返回R 6. UnaryOperator<T>函數型, T accept() 消費T, 返回T 7. BiConsumer<T, U> void accept() 消費T U 8. BiPredicate<T, U> boolean test() 判斷T U是否符合特定條件 9. ToIntFunction/ToLongFunction/ToDoubleFunction: T/ int, long, double分別計算返回此3類型的值 10. ToIntFunction/ToLongFunction/ToDoubleFunction:int, long, double / R 參數分別為int, long, double, 返回R ```java List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5); // 1. consumer @Test public void test1() { Consumer<Integer> consumer = t -> System.out.println(t); consumer.accept(10); list1.stream().forEach(consumer); } // 2. predicate @Test public void test2() { Predicate<Integer> predicate = t -> t % 2 == 0; System.out.println(predicate.test(2)); list1.stream().filter(predicate).forEach(t -> System.out.println("EVENT NUMBER: " + t)); } // 3. supplier @Test public void test3() { Supplier<String> supplier = () -> "message from supplier"; System.out.println(supplier.get()); List<String> list2 = Arrays.asList("a", "b", "c"); List<String> list3 = Arrays.asList(); System.out.println(list2.stream().findAny().orElseGet(supplier)); list3.stream().findAny().orElseGet(supplier); } ``` ```java // 1. consumer Consumer<Integer> consumer = t -> System.out.println(t); consumer.accept(10); List<Integer> list = Arrays.aslist(1,2,3,4,5); list.stream().forEach(consumer); // 2. supplier // 3. predicate Predicate<Integer> predicate = t -> {if(t %2 ==0){ return true} } ``` ## 3. method and constructor references - format: class or object :: method name - - 3 conditions - - object :: non static method ```java // Consumer void accept(T t) // PrintStream void println(T t) public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("taipei"); PrintStream ps = System.out; Consumer<String> con2 = ps::print; con2.accept("Taipei"); } ``` - - class :: static method ```java // Comparator中的int compare(T t1,T t2) // Integer中的int compare(T t1,T t2) public void test2() { Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2); System.out.println(com1.compare(1, 10)); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(1, 10)); } ``` - - class :: instance method ```java // 第一個參數是方法調用者, 第2個參數是其參數 public void test3() { Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc", "abc")); Comparator<String> com2 = String::compareTo; System.out.println(com2.compare("ab", "aa")); } ``` - 構造器引用(constructor reference) class :: new 語法糖 ```java // 參數列表一致, 且返回為constructor的對象 public void test2() { Function<Integer, Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); Function<Integer, Employee> func2 = Employee::new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } ``` - 數組引用(reference) type :: new ```java public void test4() { Function<Integer, String[]> func1 = len -> new String[len]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); Function<Integer, String[]> func2 = String[]::new; String[] arr2 = func1.apply(10); System.out.println(Arrays.toString(arr2)); } ``` - - Abstract method and method reference have the same args list and return type <抽象方法和方法引用有同樣的參數列表及回傳值類型> ## 4. Stream API (創建 - 操作 - 終止) - 針對集合執行過濾、排序、查找、映射等複雜批次操作(免於在loop處理) - 作為計算用途,return持有結果的新**stream**,不會改變原先的對象 - 便於parallel及sequnential進行操作 ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/39888fe8-cb7f-4b89-84a3-aab73a3e991f/Untitled.png) ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/21939172-5863-4aa3-96c9-22c703ceec34/Untitled.png) ref: ****[Java: Curious case of parallelStream() being slower than stream()!](https://medium.com/@hemanthgopi/java-curious-case-of-parallelstream-being-slower-than-stream-a13896bb7aac)**** - 需要結果時才會執行 - 操作的三步驟 1. source: 集合、數組,取得一個stream 2. 中間操作: 操作鏈,像是filtering、sorting、type convertion、mapping等 3. 終止操作: 終端操作後獲取操作結果,像是count, sum, collection等集結型的結果 ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1764d3ca-11f3-4584-9813-7d25e2ff7954/Untitled.png) 一、創建方法 1. 集合 collection ```java public void test1() { List<Employee> employees = EmployeeData.getEmployees(); // default Stream<E> stream() sequential stream Stream<Employee> stream = employees.stream(); // default Stream<E> parallelStream() parallel stream 併行 Stream<Employee> parallelStream = employees.parallelStream(); } ``` 2. 陣列 arrays: Arrays的靜態方法stream ```java public void test2() { // 基本型別, 有對應基本類型的陣列像是IntStream, LongStream, DoubleStream int[] arr = new int[]{1, 2, 3, 4, 5, 6}; // Arrays class: static <T> Stream<T> stream(T[] array) return stream IntStream stream = Arrays.stream(arr); // 引用型別 Employee e1 = new Employee(1001, "Tom"); Employee e2 = new Employee(1002, "Jerry"); Employee[] arr1 = new Employee[]{e1, e2}; Stream<Employee> stream1 = Arrays.stream(arr1); } ``` 3. 流 stream: 可以接收任意數量的參數 ```java public void test3() { // public static <T > Stream < T > of(T...values) return stream Stream.of(1, 2, 3, 4, 5); } ``` 4. 無限流 infinite stream: 用靜態方法創建 ```java public void test4() { // iterate: public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println); // generate: public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out::println); } ``` 二、中間操作 >除非觸發終止程序, 否則中間操作不執行, 是一種惰性求值的方式 1. 篩選與切片 - **filter**(Predicate p): 排除元素 - distinct(): 使用hashCode(), equals() 去除重複元素 - limit(long maxSize): 截斷以限定元素不超過給定數量 - skip(long n): 掠過前面n個元素, 若不足n則返回空stream, 與limit相互補 ```java public void test1() { List<Employee> list = EmployeeData.getEmployees(); // filter(Predicate p) 接收Lambda 从流中排除某些元素 Stream<Employee> stream = list.stream(); stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); // limit(long maxSize) 截断流,使其元素不超过给定数量 list.stream().limit(3).forEach(System.out::println); // skip(long )跳过元素,返回一个扔掉了前-n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 list.stream().skip(3).forEach(System.out::println); // distinct() 筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素 list.add(new Employee(1010, "刘强东", 40, 8000)); list.add(new Employee(1010, "刘强东", 40, 8000)); list.stream().distinct().forEach(System.out::println); } ``` 2. 映射 - **map(Function f)**: 將每個參數應用到給定的函數, 並映射成一個新的元素, 1對1mapping - mapToDouble(ToDoubleFunction f): 同上, 但產生一個新的DoubleStream - mapToInt(ToIntFunction f): 同上, 但產生一個新的IntStream - mapToLong(ToLongFunction f): 同上, 但產生一個新的LongStream - **flatMap(Function f)**: 取Stream<Stream<T>>, 回傳Stream<R>把各個流串起來, 沒有多層結構, 屬於1對多mapping ```java public void test2() { // map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 List<String> list = Arrays.asList("aa", "bb", "cc"); Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s -> { s.forEach(System.out::println); }); // flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } public static Stream<Character> fromStringToStream(String str) { ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()) { list.add(c); } return list.stream(); } ``` ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aba3def0-fe77-4c39-b884-3013b09b0447/Untitled.png) ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4a7def45-8947-49fc-a2ad-cc2b667b7a90/Untitled.png) 3. 排序 - sorted(): 按自然排序, 產生新的stream - sorted(Comparator com): 按比較器排列, 產生新的stream ```java public void test4() { List<Integer> list = Arrays.asList(1, 13, 2, 26, 3, 39); list.stream().sorted().forEach(System.out::println); // Class need to implement Comparable interface List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted((e1, e2) -> { int ageValue = Integer.compare(e1.getAge(), e2.getAge()); if (ageValue != 0) { return ageValue; } else { return Double.compare(e1.getSalary(), e2.getSalary()); } }). forEach(System.out::println); } ``` 三、終止操作 > 生成結果, 可以是任何非stream的值, 如List. 抑或是void > 終止後就不能再次使用 1. match & search - allMatch(Predicate p): 全部元素符合 - anyMatch(Predicate p): 至少符合一個元素 - noneMatch(Predicate p): 沒有任何符合的元素 - findFirst(): 返回第一個元素 - findAny(): 返回任意元素 - count() - max(Comparator c) - min(Comparator c) - **forEach**(Consumer): 內部迭代 2. 歸約 - reduce(T iden, BinaryOperator b): 反覆結合, 像是累加 - reduce(BinaryOperator b): 反覆結合, 像是累加, 返回Optional(T) > map-reduce模式, google用來做網路搜尋 ```java * @create 2023- 07-09 下午 02:00 */ public class MapReduce { @Test public void test1() { List<Integer> numbers = Arrays.asList(1, 3, 5, 7, 9); List<String> words = Arrays.asList("hello", "java", "lambda8"); int sum = numbers.stream().reduce(0, (a, b) -> a + b); System.out.println(sum); Optional<Integer> reduceSumWithMethodRef = numbers.stream().reduce(Integer::sum); System.out.println(reduceSumWithMethodRef.get()); Integer max = numbers.stream().reduce(0, (a, b) -> a > b ? a : b); System.out.println(numbers.stream().reduce(Integer::max).get()); String longestString = words.stream().reduce((w1, w2) -> w1.length() > w2.length() ? w1 : w2).get(); System.out.println(longestString); } } ``` 1. collect - collect(Collector c): 實現Collector方法, 將stream轉換成其他形式, 大總匯 - toList - toSet - toCollection - counting - summingInt - averageingInt - summarizeingInt: 收集Integer屬性的統計值 - collectingAndThen - groupingBy: 根據屬性作分組, 屬性為k, 結果為v - partitioningBy: 根據true and false進行分區 ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1b2b9d0a-f442-4381-ac23-ead8402c2761/Untitled.png) ## 5. Optional class - 處理NullPointerException(runtime時才會出現),常見的執行失敗原因,先預配一個對象 - 屬於一個容器類,內容物可為null或non-null,有的話isPresent()回傳true,並且能用get()取得內容對象 - 創建方法 - Optional.of(T t) : 創建的instance為非空optional - Optional.empty() : 創建空的instance - Optional.ofNullable(T t): 創建的instance為可為空optional - 判斷方法 - boolean isPresent(): 判斷值是否存在 - void ifPresent(Consumer<? super T> consumer): 如果值存在, 將其傳給consumer處理 - 獲取內容 - T get() - T orElse(T other): get值, 若無則返回 other - T orElseGet(Supplier<? extends T> other): get值, 不然就執行supplier get值 - T orElseThrow(Supplier<? extends X> exceptionSupplier): get值, 不然就拋錯 - 處理方法 - filter(Predicate<? super T> predicate) - filter(Predicate<? super T> predicate) ```java public void test1() { Girl girl = new Girl(); girl = null; Optional<Girl> optionalGirl = Optional.of(girl); } public void test2() { Girl girl = new Girl(); girl = null; Optional<Girl> optionalGirl = Optional.ofNullable(girl); // orElse(T t) return t if not null // else return orElse t1 Girl girl1 = optionalGirl.orElse(new Girl("Jessi")); System.out.println(girl1); } public void test3() { Boy boy = new Boy(); boy = null; String girlName = getGirlName(boy); System.out.println(girlName); } public String getGirlName(Boy boy) { if (boy != null) { Girl girl = boy.getGirl(); if (girl != null) { return girl.getName(); } } return null; } public String getGirlName2(Boy boy) { Optional<Boy> boyOptional = Optional.ofNullable(boy); Boy boy1 = boyOptional.orElse(new Boy(new Girl("cute"))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); girlOptional.orElse(new Girl("beauty")); return girl.getName(); } public void test5(){ Boy boy = null; boy = new Boy(); String girlName = getGirlName2(boy); System.out.println(girlName); } ``` 便於concurrent running