Java 8 Streams:多個過濾器與復雜條件
一、概述
在本文中,我們將比較過濾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:
因此,我們可以注意到使用複雜的條件會帶來性能提升。
但是,對於小樣本量,差異可能並不明顯。
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
的不同方法。首先,我們從可讀性的角度比較了這兩種方法。我們發現多個過濾器提供了更易於理解的過濾條件。
之後,我們從性能角度比較了解決方案。我們了解到,使用複雜的條件並因此創建更少的對象將帶來更好的整體性能。
與往常一樣,源代碼可在 GitHub 上獲得。