Try   HackMD

Notes on Java8 Stream and Lambda expression

contributed by < jeffrey.w >

Introduction

Java 1.8 新增了幾個 feature,其中一些對於提升程式可讀性很有幫助,例如 stream, lambda expression, method referencesfunctional interface

stream operations 會組成一個 stream pipelinestream pipeline 包含了一個 source、零個或一個以上的 intermediate operations (例如 filter),還有一個 terminal operation (例如 forEach) [1]

Intermediate operations

intermediate operations 是指像 filtermapdistinct 之類的 method,這些 method 會將一個 stream 轉換成另一個 stream [1]







g



Collection

Collection



stream0

Stream



Collection->stream0





filter

filter



stream0->filter





stream1

Stream



map

map



stream1->map





stream2

Stream



filter->stream1





map->stream2





filter

filter 是一個 intermediate operations,第 11 行的 filter 會讓符合條件 (p < 3) 的 elements 保留下來並返回新的 stream,在這個新的 stream 裡,每一個 element 都會符合第 11 行的條件。

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 } }
$ javac SearchFilter.java 
$ java SearchFilter
1
2

distinct

如果要對一個 collection 取一個 distinct 的集合,用 for 硬寫雖然也寫得出來,但是用 distinct 這個 intermediate operations,可以寫出更簡潔更優雅的 code,像第 11 行

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}

$ javac SearchDistinct.java 
$ java SearchDistinct
1
2
3

mapToInt, sorted

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 } }
$ javac Sorted.java
$ java Sorted 2 50 45
2
45
50

Terminal operations

count

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); } }
$ 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 的時機,OracleThe Java™ Tutorials 有列出 9 種理想的 use case [2]。以下針對這 9 種情境實驗一下

Approach 1: Create Methods That Search for Members That Match One Characteristic

第 6 行的寫法就比用 for(String str : args) 更容易看出意圖

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

第 8 行的 p -> checker.test(p) 就比 p -> Integer.valueOf(p) >= 1 && Integer.valueOf(p) < 18 容易維護。未來要修改也比較容易,換掉第 4 行的 implementation 就可以了。

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; } }
$ javac SearchSpecify.java
$ java SearchSpecify 1 20 15 12
1
15
12

Approach 4: Specify Search Criteria Code in an Anonymous Class

第 4 行的 Checker checker = new Checker() {.. 是使用 anonymous class

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); }
$ javac SearchAnonymous.java
$ java SearchAnonymous 1 20 15 12
1
15
12

Functional Interface

第 3~5 行結合 functional interface 和 lambda expression

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); }
$ javac SearchFuncInterface.java 
$ java  SearchFuncInterface  1 20 15 12
1
15
12

References

9.6.4.9. @FunctionalInterface
9.8. Functional Interfaces
Annotation Type FunctionalInterface

References

tags: java8 java 8 stream lambda expression