Java Function中的容易被忽略的方法identity()
/* * 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