使用Lambda表达式在Java中进行流过滤
Java提供了一些复杂的方法,对流和lambda表达式进行最佳利用。这些方法允许你使用函数式编程原则建立一个装配线。
其中一个方法,filter(),是一个中间操作,它从流中接收数据,并在根据条件改变数据后产生一个新流。
简介
在本指南中,我们将看看如何充分地使用这个方法。
前提条件
在学习本教程之前,读者应该。
- 具备Java编程语言的基本知识。
- 有一些使用Java流的工作经验。
- 了解maven的基础知识。
Java中的流过滤方法概述
Java流的filter()函数允许你根据一个标准缩小流中的项目。如果你只想要那些在你的列表中的项目,你可以使用filter方法来做到这一点。这个方法接受一个谓词作为输入,并返回一个作为该谓词结果的元素的列表。
可以用filter()方法从这个流中获得符合特定谓词的项目流。这是一个介于两者之间的过程。执行像filter()这样的中间操作并没有真正过滤任何东西,而是生成了一个新的流。这个流在浏览时,包括第一个流中满足所提供的谓词的项目。这些操作始终是懒惰的。
使用流的过滤方法
Java stream提供了filter()方法,它允许你根据你指定的谓词来过滤流元素。通过使用过滤器方法,你可以方便地从你的列表中只获得偶数元素。这个方法接受一个谓词作为参数,并返回一个作为谓词结果的元素流。
一个流接口的filter()方法可以识别流中满足某个标准的元素。它是一个流接口的中间操作。
下面是Stream filter()函数的方法签名。
Stream<q> filter(Predicate<? super q> predicate)
注意它如何接受一个谓词对象作为参数。一个谓词是一个功能接口的逻辑接口。因此,你也可以向这个函数发送一个lambda表达式。
集合过滤
filter()函数经常被用来处理集合。我们可以用它来创建一个获得90分以上的工人列表,将谓词指定为lambda。
// Assume Employee to be a POJO (plain old java object) with the employee's identity and marks
Employee george = new Employee("George", 91);
Employee mike = new Employee("Mike", 95);
List<Employee> employees = List.of(
george,
mike,
new Employee("Debra", 80),
new Employee("Robbert", 50)
);
List<Employee> employeeWith90MarksAndAbove = employees
.stream()
.filter(q -> q.getMarks() > 90)
.collect(Collectors.toList());
此外,也可以使用方法引用,这也是这种lambda表达式的简写。
Employee george = new Employee("George", 91);
Employee mike = new Employee("Mike", 95);
List<Employee> employees = List.of(
george,
mike,
new Employee("Debra", 80),
new Employee("Robbert", 50)
);
List<Employee> employeeWith90MarksAndAbove = employees
.stream()
.filter(Employee::hasOverNinetyMarks)
.collect(Collectors.toList());
在这个特殊的例子中,我们通过添加hasOverNinetyMarks方法来增强我们的Employee类。
public boolean hasOverNinetyMarks()
{
return this.marks > 90;
}
当我们使用这两种方法时,我们会得到相同的结果。
assert employeeWith90MarksAndAbove.contains(george) && employeeWith90MarksAndAbove.contains(mike);
assert employeeWith90MarksAndAbove.size() == 2;
assert关键字保证了程序中任何假设的准确性;当我们执行一个断言时,它被假定为真实。如果断言是不真实的,JVM将引发一个断言错误。请确保为你的虚拟机设置-ea 选项(启用断言),以便能够使用断言。
根据各种标准过滤数据
此外,我们可以利用过滤器的几个标准来实现我们的优势。
例如,我们可能会使用点数和名称的组合来缩小结果。
Employee george = new Employee("George", 91);
Employee mike = new Employee("Mike", 95);
List<Employee> employees = List.of(
george,
mike,
new Employee("Debra", 80),
new Employee("Robbert", 50)
);
List<Employee> georgeWith90MarksAndAbove = employees
.stream()
.filter(q -> q.getMarks() > 90 && q.getIdentity().startsWith("George"))
.collect(Collectors.toList());
assert georgeWith90MarksAndAbove.size() == 1;
assert georgeWith90MarksAndAbove.contains(george)
解释
我们用filter()的多个条件,如分数和雇员的身份。
处理异常情况的方法
filter方法是用来评估那些在评估时不抛出异常的谓词。Java编程语言的功能接口没有规定任何种类的异常,无论是检查还是不检查。JDK给出的功能接口不足以处理异常;在处理异常时,所产生的代码会变得很复杂,很麻烦。
接下来将使用几种替代方法来详细探讨λ表达式中的异常处理问题。
使用一个自定义的包装器
为我们的Employee对象添加一个profilePictureUrl ,将是我们做的第一件事。
private String profilePictureUrl;
此外,我们将创建一个简单的hasValidProfilePicture() 函数来验证个人资料图片是否仍然有效。
public boolean hasValidProfilePicture() throws IOException
{
URL url = new URL(this.profilePictureUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
}
当调用hasValidProfilePicture() 这个方法时,会发出一个IOException。现在,如果我们试图根据这个标准对客户进行排序。我们将得到以下结果。
List<Employee> employeesWithValidProfilePicture = employees
.stream()
.filter(Employee::hasValidProfilePicture)
.collect(Collectors.toList());
解释
用hasValidProfile这个方法来过滤员工,会给我们带来一个编译错误。
java: incompatible thrown types java.io.IOException in functional expression
如下图所示,处理的方法之一是将其封装在try-catch块中。
List<Employee> employeesWithValidProfilePicture = employees
.stream()
.filter(q ->
{
try {
return q.hasValidProfilePicture();
} catch (IOException x) {
// needs to take care of the stated exception
return false;
}
}).collect(Collectors.toList());
为了防止我们的谓词产生的异常被捕获,你可以把它封装在一个未检查的异常中,如来自RuntimeException的异常。
使用 ThrowingFunction
我们也可以使用ThrowingFunction 库作为替代。
使用ThrowingFunction(这是一个免费的开源包,可以下载),我们可以相对简单地处理Java函数式接口中的检查性异常。
第一步是在我们的pom.xml文件中包含抛出函数的依赖关系。
<dependency>
<groupId>pl.touk</groupId>
<artifactId>throwing-function</artifactId>
<version>1.3</version>
</dependency>
通过ThrowingPredicate 类,谓词中的异常处理被简化了,该类还包含了用于封装检查过的异常的unchecked()方法。
这个动作在下面的代码中有所说明。
List<Employee> employeesWithValidProfilePicture = employees
.stream()
.filter(ThrowingPredicate.unchecked(Employee::hasValidProfilePicture))
.collect(Collectors.toList());
结语
在本教程中,我们研究了如何使用Java中的filter()方法来过滤掉流中的特定项目。在这个过程中,我们使用了lambda表达式来指定要过滤的谓词。除此之外,我们还研究了处理异常情况的不同方法。