/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.util.function; import java.util.Objects; /** * Represents a function that accepts one argument and produces a result. * 表示接受一個參數並產生一個結果的函數。 * * @param <T> the type of the input to the function * 參數<T>是函數的輸入類型 * @param <R> the type of the result of the function * 參數<R>是函數的返回類型 * @since 1.8 */ // 注明是函數式接口 @FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * 將此函數應用於給定參數。 * @param t 函數參數 * @return R 函數返回類型 */ R apply(T t); /** * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @throws NullPointerException if before is null * * @see #andThen(Function) */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * 返回一個組合函數,該函數首先將該函數應用於其輸入,然后將該函數應用於結果。如果對任一函數的求值拋出異常,則將其中繼到組合函數的調用方。 * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null * * @see #compose(Function) */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } /** * Returns a function that always returns its input argument. * 該方法返回一個函數,該函數返回輸入的參數。 * @param <T> the type of the input and output objects to the function * @return a function that always returns its input argument */ static <T> Function<T, T> identity() { return t -> t; } }
之前只了解Function是函數式接口,支持Lambda表達式,今日由於需要,才了解到Function
的identity()
。
Function
的identity()
返回t -> T
,即本身。
也就是說identity()
可以換為t -> T
今天的需求大概是:
已經有了List<User>
,利用stream()獲取一個鍵值對Map<id, User>
。
// 構造Map鍵值對,key:Integer, value:IndexEntity // key為指標實體的id,value為對應的指標實體 Map<Integer, IndexEntity> map = indexEntities.stream().collect(Collectors.toMap(IndexEntity::getId, Function.identity()));
IndexEntity::getId
是Java8新出的方法引用。
java8 特性 Function.identity()
Function.identity()是什么?
-
// 將Stream轉換成容器或Map
-
Stream< String> stream = Stream.of("I", "love", "you", "too");
-
Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
Function是一個接口,那么Function.identity()是什么意思呢?解釋如下:
Java 8允許在接口中加入具體方法。接口中的具體方法有兩種,default方法和static方法,identity()就是Function接口的一個靜態方法。
Function.identity()返回一個輸出跟輸入一樣的Lambda表達式對象,等價於形如t -> t
形式的Lambda表達式。
identity() 方法JDK源碼如下:
-
static Function identity() {
-
return t -> t;
-
}
Function.identity()的應用
下面的代碼中,Task::getTitle
需要一個task並產生一個僅有一個標題的key。task -> task
是一個用來返回自己的lambda表達式,上例中返回一個task。
-
private static Map<String, Task> taskMap(List<Task> tasks) {
-
return tasks.stream().collect(toMap(Task::getTitle, task -> task));
-
}
可以使用Function
接口中的默認方法identity
來讓上面的代碼代碼變得更簡潔明了、傳遞開發者意圖時更加直接,下面是采用identity
函數的代碼。
-
import static java.util.function.Function.identity;
-
-
private static Map<String, Task> taskMap(List<Task> tasks) {
-
return tasks.stream().collect(toMap(Task::getTitle, identity()));
-
}
Function.identity() or t->t?
-
Arrays.asList( "a", "b", "c")
-
.stream()
-
.map(Function.identity()) // <- This,
-
.map( str -> str) // <- is the same as this.
-
.collect(Collectors.toMap(
-
Function.identity(), // <-- And this,
-
str -> str)); // <-- is the same as this.
上面的代碼中,為什么要使用Function.identity()
代替str->str
呢?它們有什么區別呢?
在上面的代碼中str -> str
和Function.identity()
是沒什么區別的因為它們都是t->t
。但是我們有時候不能使用Function.identity
,看下面的例子:
-
List list = new ArrayList<>();
-
list.add(1);
-
list.add(2);
下面這段代碼可以運行成功:
int[] arrayOK = list.stream().mapToInt(i -> i).toArray();
但是如果你像下面這樣寫:
int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();
運行的時候就會錯誤,因為mapToInt
要求的參數是ToIntFunction
類型,但是ToIntFunction
類型和Function
沒有關系
《Java8新特性》之Lambda表達式、函數式接口、方法引用、Optional
1、Java8 Lambda表達式
Lambda表達式也稱為閉包,它允許我們把函數當作參數一樣傳遞給某個方法,或者把代碼本身當作數據處理。
早期Java開發者只能使用匿名內部類來實現Lambda表達式。
最簡單的可以由逗號分隔的參數列表、->
符號、語句塊三部分組成。
例如:
// 例子1 // 參數e的類型是編譯器推理出來的 Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) ); // 例子2 // 當然也可以將執行參數的類型寫上 Arrays.asList( "a", "b", "d" ).forEach((String e)-> System.out.println( e ) ); // 例子3 // 當有多個參數時 Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2)); // 例子4 // 當Lambda的語句塊只有一行時,可以不使用return語句。 Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));
ps: 切記當有多個參數,或需要指定參數類型的時候,參數列表要加括號。
2、 函數式接口
函數式接口(Functional Interface
)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。
作用: 這樣的接口可以隱式轉換為Lambda表達式。
只要某個開發者在該接口中添加一個函數,則該接口就不再是函數式接口,進而導致編譯失敗。為了客服這種問題,並顯式說明某個接口是函數式接口,Java8提供了一個特殊的注解**@FunctionalInterface**Java 庫中的所有相關接口都已經帶有這個注解了。
@FunctionalInterface interface Addtions { int test(int a, int b);// 我是核心 default void hello() { System.out.println("我不會影響到函數式接口的定義"); } static void hello1(){ System.out.println("我也不會影響到函數式接口的定義"); } }
常用的幾個接口:
- java.util.function.Function
R apply(T t);
- java.util.function.Supplier
T get();
- java.util.function.Predicate
boolean test(T t);
- java.util.function.Consumer
void accept(T t);
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.lang.reflect.InvocationHandler
寫lamdba表達式時會經常用到四個標黑的函數式接口,重點是她們方法的返回值和方法參數。
3、接口的默認方法和靜態方法
Java 8允許我們給接口添加一個非抽象的方法實現,只需要使用 default
關鍵字即可,這個特征又叫做擴展方法,示例如下:
- 默認方法可以被實現類重寫Override
class FunctionalInterfaceTest implements Formula{ @Override public double calculate(int a) { return 0; } // 可以重寫sqrt方法。 @Override public double sqrt(int a) { return Formula.super.sqrt(a); } } @FunctionalInterface interface Formula { double calculate(int a); // 該方法(默認方法)可以被實現類重寫 default double sqrt(int a) { return Math.sqrt(a); } static void hello1(){ System.out.println("我是新來的(JAVA8),我叫靜態方法,"); } }
4、方法引用
方法引用使得開發者可以直接引用現存的方法、Java類的構造方法或者實例對象。方法引用和Lambda表達式配合使用,使得java類的構造方法看起來緊湊而簡潔,沒有很多復雜的模板代碼。
可見使用Lambda表達式的寫法和使用方法引用的寫法的效果是一樣的,但是使用方法引用有時會更加簡化代碼
- 構造器引用
類名::new
- 靜態方法引用
類名::靜態方法
- 對象方法引用
類名::方法
- 當Lambda表達式的參數列表第一個參數為實例方法的調用者,第二個參數(或無參)是實例方法的參數時,可以使用這種方法。
- 實例方法引用
實例對象::成員方法
- 要先獲取一個實例對象
public class Test { private String name; public String getName() { return this.name; } public Test(String name) { this.name = name; } public static String staticMethod(){ return "我是靜態方法!"; } public static void main(String[] args) { Test test1 = new Test("小明"); // Lambda表達式 Supplier<String> func1 = () -> test1.getName(); System.out.println("Lambda表達式測試:" + func1.get()); // 實例方法引用 Supplier<String> func2 = test1::getName; System.out.println("方法引用方式測試:" + func2.get()); // 靜態方法引用 Supplier<String> func3 = Test::staticMethod; System.out.println("靜態方法引用測試:" + func3.get()); // 構造方法引用(構造器引用) Function<String, Test> func4 = Test::new; Test test2 = func4.apply("xxx"); System.out.println("構造方法引用測試:" + test2); // 對象方法引用 // Test為類名,getName為成員方法。 Function<Test, String> func5 = Test::getName; System.out.println("對象方法測試引用:" + func5.apply(test1)); } }
5、Optional
Java應用中最常見的bug就是NullPointerException,
就比如比較兩個字符串是否相等
s1.equals(s2)
,如果s1==null
,那么一運行,console
立馬就爆紅了。
所以Java8提供了Optional來解決這問題。
- isPresent(): 如果Optional實例持有一個非空值,方法返回true,否則返回false
- orElseGet():,Optional實例持有null,則可以接受一個lambda表達式生成的默認值
- map(): 可以將現有的Opetional實例的值轉換成新的值
- orElse(): Opetional 實例持有null的時候返回傳入的默認值, 方法與orElseGet() 方法類似。
- filter(): 如果optional實例不為null,並且filter中lambda表達式返回true,就返回一個Optional實例;反之返回一個空optional。
-
If a value is present, and the value matches the given predicate,return an {@code Optional} describing the value, otherwise return an empty {@code Optional}.
-
- 當optional實例為null時
Optional< String > fullName = Optional.ofNullable( null ); System.out.println( "Full Name is set? " + fullName.isPresent() ); System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) ); // 下面為輸出結果 Full Name is set? false Full Name: [none] Hey Stranger!
2.當optional實例不為null時
Optional< String > firstName = Optional.of( "Tom" ); System.out.println( "First Name is set? " + firstName.isPresent() ); System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" )); //輸出結果 First Name is set? true First Name: Tom Hey Tom!
探索Java8:(一)Stream的使用
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數據。
Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
Stream API可以極大提高Java程序員的生產力,讓程序員寫出高效率、干凈、簡潔的代碼。
這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的結果。
什么是 Stream?
Stream(流)是一個來自數據源的元素隊列並支持聚合操作
- 元素是特定類型的對象,形成一個隊列。 Java中的Stream並不會存儲元素,而是按需計算。
- 數據源 流的來源。 可以是集合,數組,I/O channel, 產生器generator 等。
- 聚合操作 類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作還有兩個基礎的特征:
- Pipelining: 中間操作都會返回流對象本身。 這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。 這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting)。
- 內部迭代: 以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。 Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。
一、流的創建
- stream() − 為集合創建串行流。
- parallelStream() − 為集合創建並行流。parallelStream其實就是一個並行執行的流.它通過默認的ForkJoinPool,可能提高你的多線程任務的速度。並行流在遍歷時可能是無序的。
public class ParallelStream { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.stream().forEach(System.out::print); } }
二、 forEach、map、filter、limit、sorted
numbers.stream().forEach(System.out::print);
numbers.stream().forEach(i->System.out.print(i));
上述兩種方法是等價的。
forEach
forEach用來對stream中的數據進行迭代,比如上面創建流的操作就使用了forEach。看會上面的例子后理解forEach不會很難的。需要注意的是,forEach操作是不能改變遍歷對象本身的。
Map
map 方法用於映射每個元素到對應的結果,多數情況下用來處理數據。下面給出一個讓原list個位置元素自增2的代碼:
public class MapDemo { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); List<Integer> outPutList = numbers.stream().map(i -> i + 2).distinct().collect(Collectors.toList()); outPutList.forEach(n->System.out.print(n+" ")); } }
filter
filter 方法用於通過設置的條件過濾出元素。以下代碼片段使用 filter 方法過濾出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 獲取空字符串的數量 int count = strings.stream().filter(string -> string.isEmpty()).count();
Limit
limit 方法用於獲取指定數量的流。 以下代碼片段使用 limit 方法打印出 10 條數據:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
還有一個常用的是配合skip()
方法用來進行分頁操作。
int pageSize=10; int currentPage=1; return pageList.stream() .skip(pageSize * (currentPage-1)) .limit(pageSize) .collect(Collectors.toList());
sorted
sorted 方法用於對流進行排序。以下代碼片段使用 sorted 方法對輸出的 10 個隨機數進行排序:
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
Collectors
Collectors 可用於返回列表或字符串,上面介紹map的例子就用到了Collectors,下面給出菜鳥教程的一個例子:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("篩選列表: " + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("合並字符串: " + mergedString);
三、 統計
顧名思義,統計就是用來統計數據的,一般用於int、double、long等基本類型上。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的數 : " + stats.getMax()); System.out.println("列表中最小的數 : " + stats.getMin()); System.out.println("所有數之和 : " + stats.getSum()); System.out.println("平均數 : " + stats.getAverage());
Stream先介紹到這里,我們Stream代碼覺得陌生是因為剛接觸聲明式編程的風格,下一篇應該會介紹lambda表達式和Optional的用法,我們會更多地用聲明式的編程風格。
探索Java8:(二)Function接口的使用
Java8 添加了一個新的特性Function,顧名思義這一定是一個函數式的操作。我們知道Java8的最大特性就是函數式接口。所有標注了@FunctionalInterface
注解的接口都是函數式接口,具體來說,所有標注了該注解的接口都將能用在lambda表達式上。
標注了@FunctionalInterface
的接口有很多,但此篇我們主要講Function,了解了Function其他的操作也就很容易理解了。
@FunctionalInterface public interface Function<T, R> { R apply(T t); /** * @return a composed function that first applies the {@code before} * function and then applies this function */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * @return a composed function that first applies this function and then * applies the {@code after} function */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } }
為了方便地閱讀源碼,我們需要了解一些泛型的知識,如果你對泛型已經很熟悉了,那你可以跳過這段 。
泛型是JDK1.5引入的特性,通過泛型編程可以使編寫的代碼被很多不同的類型所共享,這可以很好的提高代碼的重用性。因為本篇重點不是介紹泛型,所以我們只關注上述Function源碼需要用到的泛型含義。
1. 泛型類
泛型類使用<T>
來表示該類為泛型類,其內部成員變量和函數的返回值都可以為泛型<T>
,Function源碼的標識為<T,R>
,也就是兩個泛型參數,此處不再贅述,具體泛型類可以看網上的文章。
2. 泛型方法和通配符
在方法修飾符的后面加一個<T>
表明該方法為泛型方法,如Function 的源碼里的compose方法的<V>
。通配符也很好理解,還是compose的例子,我們可以看到compose的參數為一個Function類型,其中Functin的參數指定了其第一個參數必須是V的父類,第二個參數必須繼承T,也就是T的子類。
源碼解析
1.apply
講完了上面這些就可以開始研究源碼了。
首先我們已經知道了Function是一個泛型類,其中定義了兩個泛型參數T和R,在Function中,T代表輸入參數,R代表返回的結果。也許你很好奇,為什么跟別的java源碼不一樣,Function 的源碼中並沒有具體的邏輯呢?
其實這很容易理解,Function 就是一個函數,其作用類似於數學中函數的定義 ,(x,y)跟<T,R>的作用幾乎一致。
所以Function中沒有具體的操作,具體的操作需要我們去為它指定,因此apply具體返回的結果取決於傳入的lambda表達式。
R apply(T t);
舉個例子:
public void test(){ Function<Integer,Integer> test=i->i+1; test.apply(5); } /** print:6*/
我們用lambda表達式定義了一個行為使得i自增1,我們使用參數5執行apply,最后返回6。這跟我們以前看待Java的眼光已經不同了,在函數式編程之前我們定義一組操作首先想到的是定義一個方法,然后指定傳入參數,返回我們需要的結果。函數式編程的思想是先不去考慮具體的行為,而是先去考慮參數,具體的方法我們可以后續再設置。
再舉個例子:
public void test(){ Function<Integer,Integer> test1=i->i+1; Function<Integer,Integer> test2=i->i*i; System.out.println(calculate(test1,5)); System.out.println(calculate(test2,5)); } public static Integer calculate(Function<Integer,Integer> test,Integer number){ return test.apply(number); } /** print:6*/ /** print:25*/
我們通過傳入不同的Function,實現了在同一個方法中實現不同的操作。在實際開發中這樣可以大大減少很多重復的代碼,比如我在實際項目中有個新增用戶的功能,但是用戶分為VIP和普通用戶,且有兩種不同的新增邏輯。那么此時我們就可以先寫兩種不同的邏輯。除此之外,這樣還讓邏輯與數據分離開來,我們可以實現邏輯的復用。
當然實際開發中的邏輯可能很復雜,比如兩個方法F1,F2都需要兩個個邏輯AB,但是F1需要A->B,F2方法需要B->A。這樣的我們用剛才的方法也可以實現,源碼如下:
public void test(){ Function<Integer,Integer> A=i->i+1; Function<Integer,Integer> B=i->i*i; System.out.println("F1:"+B.apply(A.apply(5))); System.out.println("F2:"+A.apply(B.apply(5))); } /** F1:36 */ /** F2:26 */
也很簡單呢,但是這還不夠復雜,假如我們F1,F2需要四個邏輯ABCD,那我們還這樣寫就會變得很麻煩了。
2.compose和andThen
compose和andThen可以解決我們的問題。先看compose的源碼
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
compose接收一個Function參數,返回時先用傳入的邏輯執行apply,然后使用當前Function的apply。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
andThen跟compose正相反,先執行當前的邏輯,再執行傳入的邏輯。
這樣說可能不夠直觀,我可以換個說法給你看看
compose等價於B.apply(A.apply(5)),而andThen等價於A.apply(B.apply(5))。
public void test(){ Function<Integer,Integer> A=i->i+1; Function<Integer,Integer> B=i->i*i; System.out.println("F1:"+B.apply(A.apply(5))); System.out.println("F1:"+B.compose(A).apply(5)); System.out.println("F2:"+A.apply(B.apply(5))); System.out.println("F2:"+B.andThen(A).apply(5)); } /** F1:36 */ /** F1:36 */ /** F2:26 */ /** F2:26 */
我們可以看到上述兩個方法的返回值都是一個Function,這樣我們就可以使用建造者模式的操作來使用。
B.compose(A).compose(A).andThen(A).apply(5);
這個操作很簡單,你可以自己試試。
探索Java8:(三)Predicate接口的使用
上一篇學習了下Function接口的使用,本篇我們學習下另一個實用的函數式接口Predicate。
Predicate的源碼跟Function的很像,我們可以對比這兩個來分析下。直接上Predicate的源碼:
public interface Predicate<T> { /** * Evaluates this predicate on the given argument. */ boolean test(T t); /** * Returns a composed predicate that represents a short-circuiting logical * AND of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code false}, then the {@code other} * predicate is not evaluated. */ default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } /** * Returns a predicate that represents the logical negation of this * predicate. */ default Predicate<T> negate() { return (t) -> !test(t); } /** * Returns a composed predicate that represents a short-circuiting logical * OR of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code true}, then the {@code other} * predicate is not evaluated. */ default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } /** * Returns a predicate that tests if two arguments are equal according * to {@link Objects#equals(Object, Object)}. */ static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
Predicate是個斷言式接口其參數是<T,boolean>,也就是給一個參數T,返回boolean類型的結果。跟Function一樣,Predicate的具體實現也是根據傳入的lambda表達式來決定的。
boolean test(T t);
接下來我們看看Predicate默認實現的三個重要方法and,or和negate
default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate() { return (t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); }
這三個方法對應了java的三個連接符號&&、||和!,基本的使用十分簡單,我們給一個例子看看:
int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; List<Integer> list=new ArrayList<>(); for(int i:numbers) { list.add(i); } Predicate<Integer> p1=i->i>5; Predicate<Integer> p2=i->i<20; Predicate<Integer> p3=i->i%2==0; List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList()); System.out.println(test.toString()); /** print:[6, 8, 10, 12, 14]*/
我們定義了三個斷言p1,p2,p3。現在有一個從1~15的list,我們需要過濾這個list。上述的filter是過濾出所有大於5小於20,並且是偶數的列表。
假如突然我們的需求變了,我們現在需要過濾出奇數。那么我不可能直接去改Predicate,因為實際項目中這個條件可能在別的地方也要使用。那么此時我只需要更改filter中Predicate的條件。
List test=list.stream().filter(p1.and(p2).and(p3.negate())).collect(Collectors.toList());
/** print:[7, 9, 11, 13, 15]*/
我們直接對p3這個條件取反就可以實現了。是不是很簡單?
isEqual這個方法的返回類型也是Predicate,所以我們也可以把它作為函數式接口進行使用。我們可以當做==操作符來使用。
List test=list.stream()
.filter(p1.and(p2).and(p3.negate()).and(Predicate.isEqual(7))) .collect(Collectors.toList()); /** print:[7] */
java8 函數式接口編程:https://blog.csdn.net/qq_28410283/category_7718494.html