1. 概述
在这个教程中,我们将探讨Java流的不同过滤方式。首先,我们将关注哪种方法能产生更易读的代码。然后,我们将从性能角度来比较这些解决方案。
2. 可读性
2.1. 多个过滤器
流API允许链式应用多个过滤器。我们可以利用这一点来满足复杂过滤条件。如果需要否定条件,我们还可以使用not
谓词。这种方法将使代码清晰易懂:
public class Student {
private String name;
private int year;
private List<Integer> marks;
private Profile profile;
// constructor getters and setters
}
2.2. 单个复杂条件的过滤
另一种选择是使用一个包含复杂条件的单一过滤器。不幸的是,这会导致代码阅读起来稍显困难:
@Test
public void whenUsingSingleComplexFilter_dataShouldBeFiltered() {
List<Student> filteredStream = students.stream()
.filter(s -> s.getMarksAverage() > 50
&& s.getMarks().size() > 3
&& s.getProfile() != Student.Profile.PHYSICS)
.collect(Collectors.toList());
assertThat(filteredStream).containsExactly(mathStudent);
}
尽管如此,我们可以将条件提取到单独的方法中以改进可读性:
public boolean isEligibleForScholarship() {
return getMarksAverage() > 50
&& marks.size() > 3
&& profile != Profile.PHYSICS;
}
这样,我们将复杂的条件隐藏起来,让过滤标准更具意义:
@Test
public void whenUsingSingleComplexFilterExtracted_dataShouldBeFiltered() {
List<Student> filteredStream = students.stream()
.filter(Student::isEligibleForScholarship)
.collect(Collectors.toList());
assertThat(filteredStream).containsExactly(mathStudent);
}
当能将过滤逻辑封装在模型中时,这是一个理想的解决方案。
3. 性能
虽然多过滤器提高了代码的可读性,但会增加对象创建,可能影响性能。为了验证这一点,我们将过滤不同大小的流,并对元素进行多次检查。然后计算总处理时间并比较两种方法。测试还包括并行流和传统的for
循环:
(图片缺失,无法展示图表)
结果显示,使用复杂条件会带来性能提升。但对于小样本量,差异可能不明显。
4. 条件的执行顺序
无论使用单个还是多个过滤器,如果检查顺序不当,过滤操作可能会导致性能下降。
4.1. 过滤掉大量元素的条件
假设我们有一个包含100个整数的流,想要找到小于20的偶数。如果首先检查奇偶性,我们将进行总共150次检查,因为每次都要评估第一个条件,而第二个条件只针对偶数。
@Test
public void givenWrongFilterOrder_whenUsingMultipleFilters_shouldEvaluateManyConditions() {
long filteredStreamSize = IntStream.range(0, 100).boxed()
.filter(this::isEvenNumber)
.filter(this::isSmallerThanTwenty)
.count();
assertThat(filteredStreamSize).isEqualTo(10);
assertThat(numberOfOperations).hasValue(150);
}
相反,如果颠倒过滤条件的顺序,我们只需要120次检查就能正确过滤流。因此,应优先评估那些过滤掉大部分元素的条件。
4.2. 慢速或耗时条件
有些条件可能较慢,比如其中一个过滤器涉及执行沉重的逻辑或网络外部调用。为了优化性能,我们将尽可能减少这些条件的评估次数。基本上,应在其他所有条件都满足时才评估这些条件。
5. 总结
在这篇文章中,我们分析了Java流的不同过滤方式。从可读性的角度看,多个过滤器提供了更直观的过滤条件。从性能方面来说,使用复杂条件(从而减少对象创建)将带来整体更好的性能。
如常,源代码可在GitHub上获取。