【Java函数篇】Java 8 Predicate函数接口的用法详解
为什么介绍Predicate
自从Java8发布以后,代码里面就多了很多函数式的接口和代码。在流式的编程中,我们经常会用到Predicate和其他函数,在一些开源的代码中也会看到别人定义的Predicate方法。但其实你有没有感觉在写代码的经历中,就很少会定义Predicate方法,都是用在stream流中更多,比如下面这段代码。今天就来学习和分享一下Predicate,彻底了解和掌握Predicate的概念以及用法。
作为参考,在数学中谓词也通常被理解为布尔值函数“P:X?{true,false}”,成为X的谓词:
// Returns true if number is even; else false.
Predicate<Integer> evenPredicate = n -> n % 2 == 0; // Returns true if age is greater than or equals to 18; else false.
Predicate<Person> canVote = p -> p.age() >= 18; //Stream filtering with Predicate
List<Person> voters = personList.stream().filter(canVote).toList();
Predicate函数接口概念
我们先看下Java对Predicate的定义:
表示Predicate是一个接受一个<泛型>参数的布尔函数,返回的是true 或 false,经常被用于数据的过滤:
比如stream.filter(k -> k > 0)
示例
场景就是过滤一个list,经常会这样写:
@Testpublic void predicate() {List<People> peopleList = mockPeopleList();peopleList.stream().filter(people -> people.getAge() > 10).forEach(System.out::println);}
其中filter里这个lambda表达是就是个Predicate:
.filter(people -> people.getAge() > 10)
我们换一种写法如下:
@Testpublic void predicate() {List<People> peopleList = mockPeopleList();Predicate<People> predicate = people -> people.getAge() > 10;peopleList.stream().filter(predicate).forEach(System.out::println);}
这样比较清晰可以看到Predicate的使用方式。
什么时候使用Predicate
其实换种问法就是:在日常编程中,我们可以在哪里使用这些返回 true 或 false 的函数呢?
我们可以使用谓词在对集合中的对象进行条件评估后从集合中筛选对象,评估结果可以是 true 或 false。
例如,我们可以在以下实际用例中使用谓词:
- 找到特定日期之后出生的所有孩子
- 特定时间范围内订购的披萨
- 年龄大于特定岁数的员工
- 等等……
Predicate用法
1. 简单的Predicate
如前所述,谓词会评估表达式并返回布尔值。现在让我们通过一个简单的示例来看一下创建简单谓词的几个示例。
Predicate<Employee> isAdult = e -> e.getAge() > 18;
Predicate<Employee> isMale = p -> p.getGender().equalsIgnoreCase("M");
2. 复杂的Predicate
我们可以通过混合两个或多个谓词来创建一个复杂的谓词。可以使用and,or,negate等组合
Predicate<Employee> isAdultMale= isAdult.and(isMale);
Predicate<Employee> isAdultOrMale = isAdult.or(isMale);
或者这样:
@Testpublic void predicate() {List<People> peopleList = mockPeopleList();Predicate<People> ageGrate10 = people -> people.getAge() > 18;Predicate<People> genderMen = people -> people.getSex().equals("男");Predicate<People> levelLess5 = people -> people.getLevel() < 5;peopleList.stream().filter(ageGrate10.and(genderMen).or(levelLess5)).forEach(System.out::println);}
3. 反向谓词
可以使用 negate() 方法创建现有谓词的反向谓词。
Predicate<Employee> isMinor = isAdult.negate();
4. 在Java 8 Stream中使用Predicate
Predicate 是一个函数式接口,可以在需要谓词的任何地方将其传递到 lambda 表达式中。例如,Stream 接口中的 filter() 方法就是这样一种方法。
/*** Returns a stream consisting of the elements of this stream that match the given predicate.*/
Stream<T> filter(Predicate<? super T> predicate);
示例:
Predicate<Employee> isMale = p -> p.getGender().equalsIgnoreCase("M");
List<Employee> maleEmployeeList = employeeList.stream().filter(isMale).toList();
如果我们想使用两个参数来测试一个条件,我们也可以使用 BiPredicate 类。
BiPredicate<Integer, String> isAdultMale = (p1, p2) -> p1 > 18 && p2.equalsIgnoreCase("M");
List<Employee> adultMalesList = employeeList.stream().filter(x -> isAdultMale.test(x.getAge(), x.getGender())).toList();
总结
Predicate 提供了一种定义和使用布尔值条件作为对象的方法,从而使代码更加灵活和富有表现力。