# Notes on Java8 Stream and Lambda expression contributed by < [`jeffrey.w`](https://github.com/jeffrey-minwei/java8-stream) > ## Introduction `Java 1.8` 新增了幾個 feature,其中一些對於提升程式可讀性很有幫助,例如 `stream`, `lambda expression`, `method references` 和 `functional interface`。 `stream operations` 會組成一個 `stream pipeline`,`stream pipeline` 包含了一個 `source`、零個或一個以上的 `intermediate operations` (例如 [`filter`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#filter-java.util.function.Predicate-)),還有一個 `terminal operation` (例如 [`forEach`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer-)) <sup>[[`1`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)]</sup>。 ## Intermediate operations `intermediate operations` 是指像 `filter`、`map`、`distinct` 之類的 method,這些 method 會將一個 `stream` 轉換成另一個 `stream` <sup>[[`1`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)]</sup>。 ```graphviz digraph g { rankdir=LR; Collection[shape="rect"]; stream0[shape="rect", label="Stream"]; stream1[shape="rect", label="Stream"]; stream2[shape="rect", label="Stream"]; filter[shape="rect"]; map[shape="rect"]; Collection -> stream0 -> filter -> stream1 -> map -> stream2; } ``` ### filter `filter` 是一個 `intermediate operations`,第 11 行的 `filter` 會讓符合條件 (p < 3) 的 elements 保留下來並返回新的 `stream`,在這個新的 `stream` 裡,每一個 element 都會符合第 11 行的條件。 ```java= import java.util.List; import java.util.Arrays; public class SearchFilter { public static void main(String[] args) { List<Integer> list = Arrays.asList(new Integer[]{1, 2, 3}); list.stream() .filter(p -> p < 3) .forEach(System.out::println); // Method reference } } ``` ```shell $ javac SearchFilter.java $ java SearchFilter 1 2 ``` ### distinct 如果要對一個 collection 取一個 distinct 的集合,用 for 硬寫雖然也寫得出來,但是用 `distinct` 這個 `intermediate operations`,可以寫出更簡潔更優雅的 code,像第 11 行 ```java= import java.util.List; import java.util.Arrays; public class SearchDistinct { public static void main(String[] args) { List<Integer> list = Arrays.asList(new Integer[]{1, 2, 3, 3}); list.stream() .distinct() .forEach(System.out::println); // Method reference } } ``` {1, 2, 3, 3} 經過 `stream` 再經過 `distinct`,就是 {1, 2, 3} ```shell $ javac SearchDistinct.java $ java SearchDistinct 1 2 3 ``` ### [mapToInt](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#mapToInt-java.util.function.ToIntFunction-), [sorted](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#sorted--) ```java= import java.util.Arrays; public class Sorted { public static void main(String[] args) { Arrays.asList(args) .stream() .mapToInt(p -> Integer.valueOf(p)) .sorted() .forEach(System.out::println); // Method reference } } ``` ```shell $ javac Sorted.java $ java Sorted 2 50 45 2 45 50 ``` <!-- ### [sorted](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#sorted--) 使用自定的 [`Comparator`](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html) 做排序 --> ## Terminal operations ### count ```java= import java.util.List; import java.util.Arrays; public class SearchCount { public static void main(String[] args) { long count = Arrays.asList(args) .stream() .distinct() .count(); System.out.println(count); } } ``` ```shell $ javac SearchCount.java $ java SearchCount 1 2 3 3 4 4 $ java SearchCount 1 1 1 2 2 2 3 3 3 3 ``` ## Lambda expression 使用 `Lambda expression` 的時機,`Oracle` 的 [`The Java™ Tutorials`](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html) 有列出 9 種理想的 use case <sup>[[`2`](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)]</sup>。以下針對這 9 種情境實驗一下 ### [Approach 1: Create Methods That Search for Members That Match One Characteristic](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach1) 第 6 行的寫法就比用 `for(String str : args)` 更容易看出意圖 ```java= public class Search { public static void main(String[] args) { Arrays.asList(args) .stream() .filter(p -> p.startsWith("-")) // lambda expression .forEach(i -> System.out.println(i)); } } ``` ### [Approach 3: Specify Search Criteria Code in a Local Class](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach3) 第 8 行的 `p -> checker.test(p)` 就比 `p -> Integer.valueOf(p) >= 1 && Integer.valueOf(p) < 18` 容易維護。未來要修改也比較容易,換掉第 4 行的 `implementation` 就可以了。 ```java= public class SearchSpecify { public static void main(String[] args) { Checker checker = new CheckerAge(); Arrays.asList(args) .stream() .filter(p -> checker.test(p)) // lambda expression .forEach(System.out::println); } } interface Checker { boolean test(String str); } class CheckerAge implements Checker { public boolean test(String str){ Integer age = Integer.valueOf(str); return age >= 1 && age < 18; } } ``` ```shell $ javac SearchSpecify.java $ java SearchSpecify 1 20 15 12 1 15 12 ``` ### [Approach 4: Specify Search Criteria Code in an Anonymous Class](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach4) 第 4 行的 `Checker checker = new Checker() {..` 是使用 `anonymous class` ```java= public class SearchAnonymous { public static void main(String[] args) { Checker checker = new Checker() { public boolean test(String str){ Integer age = Integer.valueOf(str); return age >= 1 && age < 18; } }; Arrays.asList(args) .stream() .filter(p -> checker.test(p)) .forEach(System.out::println); } } interface Checker { boolean test(String str); } ``` ```shell $ javac SearchAnonymous.java $ java SearchAnonymous 1 20 15 12 1 15 12 ``` ## Functional Interface 第 3~5 行結合 functional interface 和 lambda expression ```java= public class SearchFuncInterface { public static void main(String[] args) { Checker checker = (x) -> { Integer age = Integer.valueOf(x); return age >= 1 && age < 18; }; Arrays.asList(args) .stream() .filter(age -> checker.test(age)) .forEach(System.out::println); } } @FunctionalInterface interface Checker { boolean test(String age); } ``` ```shell $ javac SearchFuncInterface.java $ java SearchFuncInterface 1 20 15 12 1 15 12 ``` ### References [9.6.4.9. @FunctionalInterface](https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6.4.9) [9.8. Functional Interfaces](https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8) [Annotation Type FunctionalInterface](https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html) <!-- ## Method reference ### Method reference 和 lambda expression 的差別 --> ## References - [ ] [The Java™ Tutorials, Lambda Expressions](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html) - [ ] [The Java™ Tutorials, Method References](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html) ###### tags: `java8` `java 8` `stream` `lambda expression`