# 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進行操作


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等集結型的結果

一、創建方法
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();
}
```


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進行分區

## 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