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