拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Java 8 Streams:多个过滤器与复杂条件

Java 8 Streams:多个过滤器与复杂条件

白鹭 - 2022-08-19 2247 0 2

一、概述

在本文中,我们将比较过滤Java Streams的不同方法。最初,我们将看看哪种解决方案会产生更易读的代码。之后,我们将从性能角度比较解决方案。

2. 可读性

首先,我们将从可读性的角度比较这两种解决方案。对于本节中的代码示例,我们将使用Student类:

public class Student {
 private String name;
 private int year;
 private List<Integer> marks;
 private Profile profile;
 // constructor getters and setters
 }

我们的目标是根据以下三个规则过滤Students流:

  • profile必须是Profile.PHYSICS

  • marks数应大于 3

  • 平均mark应大于50

2.1。多个过滤器

Stream API允许链接多个过滤器。我们可以利用它来满足所描述的复杂过滤标准。此外,如果我们想否定条件,我们可以使用not谓词。

这种方法将产生一个干净且易于理解的代码:

@Test
 public void whenUsingMultipleFilters_dataShouldBeFiltered() {
 List<Student> filteredStream = students.stream()
 .filter(s -> s.getMarksAverage() > 50)
 .filter(s -> s.getMarks().size() > 3)
 .filter(not(s -> s.getProfile() == Student.Profile.PHYSICS))
 .collect(Collectors.toList());
 assertThat(filteredStream).containsExactly(mathStudent);
 }

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. 性能

我们已经看到,使用多个过滤器可以提高代码的可读性。另一方面,这将意味着创建多个对象,并可能导致性能损失。为了证明这一点,我们将过滤不同大小的Streams并对它们的元素进行多次检查。

在此之后,我们将以毫秒为单位计算总处理时间并比较两种解决方案。此外,我们将在测试中包含Parallel Streams和简单的、旧的for loop:

java-8-streams-multiple-filters-vs-complex-condition.jpg

 因此,我们可以注意到使用复杂的条件会带来性能提升。

但是,对于小样本量,差异可能并不明显。

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 Streams的不同方法。首先,我们从可读性的角度比较了这两种方法。我们发现多个过滤器提供了更易于理解的过滤条件。

之后,我们从性能角度比较了解决方案。我们了解到,使用复杂的条件并因此创建更少的对象将带来更好的整体性能。


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *