在前幾篇文章中,我們已經熟悉了如何使用 Stream API 优雅地转换集合。然而,當我們需要「查找」符合條件的元素時,傳統的迴圈寫法不僅冗長,還容易出錯。本文將介紹 filter() 方法,展示如何用函數式風格輕鬆完成篩選操作,並探討如何避免 Lambda 表達式帶來的代碼冗餘問題。
假設我們要從名字列表中找出所有以「N」開頭的名字:
final List<String> startsWithN = new ArrayList<>();
for (String name : friends) {
if (name.startsWith("N")) {
startsWithN.add(name);
}
}
這段代碼需要手動創建列表、迭代、判斷、添加,四步操作才能完成一個簡單的篩選任務。
使用 filter() 方法,同樣的邏輯只需一行鏈式調用:
final List<String> startsWithN =
friends.stream()
.filter(name -> name.startsWith("N"))
.collect(Collectors.toList());
filter() 接受一個返回 boolean 的 Predicate,只保留滿足條件的元素,最終通過 collect() 收集為新列表。運行結果:
Found 2 names
雖然 Lambda 簡潔,但若在多處重複相同邏輯(如對不同列表都篩選「N」開頭),會導致代碼冗余,違反 DRY(Don't Repeat Yourself)原則。
final long countFriendsStartN = friends.stream().filter(name -> name.startsWith("N")).count();
final long countEditorsStartN = editors.stream().filter(name -> name.startsWith("N")).count();
final long countComradesStartN = comrades.stream().filter(name -> name.startsWith("N")).count();
三處相同的 name -> name.startsWith("N") 違反了重用原則。
filter() 參數類型是 java.util.function.Predicate<T>,我們可以顯式聲明並重用:
final Predicate<String> startsWithN = name -> name.startsWith("N");
final long countFriendsStartN = friends.stream().filter(startsWithN).count();
final long countEditorsStartN = editors.stream().filter(startsWithN).count();
final long countComradesStartN = comrades.stream().filter(startsWithN).count();
這樣邏輯只寫一次,後續直接引用變量,既消除冗余,又便於維護與性能優化。
filter() 讓集合篩選變得聲明式且高效。配合 Predicate 變量重用,能進一步提升代碼質量。下一篇將探討如何用「詞法作用域」與「閉包」解決更複雜的參數化篩選需求。