Java Streams 是 Java 8 引入後,徹底改變了開發者處理集合的方式。Java Streams 已經成為處理集合的必備工具。它們不僅讓資料處理變得更加簡潔、易讀,還顯著提升了程式碼的可維護性和開發者的生產力。儘管 filter
和 map
是基本操作,但 Java Streams API 中還有很多值得探索的高階用法。
在這篇文章中,我將介紹 5 個實用技巧,幫助你更好地運用 Java Streams。這些方法不僅能簡化複雜的資料轉換,還能讓你的程式碼更加清晰和高效。
精準過濾
入門
想象一下,你有一個產品列表,想要篩選出有趣的靈魂測試工程師。filter
操作是實現這一目標的利器。來看個例子:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<FunTester> funTesters = testers.stream()
.filter(tester -> tester.getSoul().contains("Fun"))
.collect(Collectors.toList());
}
這段程式碼從 testers
列表中建立了一個流,並透過 filter
篩選出有趣的靈魂(Teseter),最後將結果收集到一個新的 funTesters
列表中。
進階
filter
的強大之處在於你可以輕鬆地組合多個條件。例如,如果你還想篩選出特定類別(比如成年的),可以再加一個 filter
:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<FunTester> funTesters = testers.stream()
.filter(tester -> tester.getSoul().contains("Fun"))
.filter(tester -> tester.getAge() > 18)
.collect(Collectors.toList());
}
透過組合多個條件,你可以更加精準地獲取需要的結果。
map 轉換
入門
map
操作可以對流中的每個元素進行轉換。它接收一個函式(通常是 Lambda
表示式),將其應用於每個元素,生成一個包含轉換結果的新流。
來看個簡單的例子:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<String> souls = testers.stream()
.map(FunTester::getSoul)
.collect(Collectors.toList());
}
在這個例子中,我們使用 map
提取每個 Tester
的 soul
,生成一個 String
的列表。
進階
我們可以在 map
之前或之後新增 filter 操作,來篩選和轉換資料。例如,只處理非空的 soul 屬性:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<String> souls = testers.stream()
.map(FunTester::getSoul)
.filter(Objects::nonNull) // 過濾掉空值
.map(String::trim) // 去除首尾空格
.collect(Collectors.toList());
}
map
的用途非常廣泛,不僅限於簡單的資料提取,還能用於更復雜的轉換。
彙總
入門
reduce
操作可以將流中的元素累積成一個單一結果,非常適合進行統計或資料彙總。比如,計算所有 Tester 的 BUG 總數:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
int bugs = testers.stream()
.mapToInt(FunTester::getBugs)// 獲取所有測試工程師的 bug 數量
.reduce(0, Integer::sum);// 計算總 bug 數量
}
這段程式碼演示瞭如何使用 Java Streams API 高效地計算 List 中所有 FunTester 物件的 BUG 總數。使用 mapToInt 提取 int 值並透過 reduce 計算總和是處理這類問題的常見模式。這種方法簡潔而強大,適用於各種類似的資料處理場景。
進階
reduce
還能用於其他型別的彙總操作。比如,找出最便宜的商品:
FunTester funTester = testers.stream()
.reduce((t1, t2) -> t1.getBugs() > t2.getBugs() ? t1 : t2)
.get();
System.out.println("BUG 最多的靈魂測試工程師是:" + funTester.getName());
在這個例子中,reduce
用於比較每個 Tester 的 BUG 數量,找出 BUG 最多的那個,把他揪出來。
分組
基礎
groupBy 是 Java Streams API 中的一個強大功能,它允許你根據某個特性將流中的元素分組。透過 groupBy,你可以將具有相似屬性的元素歸類到同一個集合中,從而方便地進行進一步的分析和處理。
比如將 Tester 根據司齡進行分組:
Map<Integer, List<FunTester>> collect = testers.stream()
.collect(Collectors.groupingBy(FunTester::getCompanyYears));
程式碼演示瞭如何使用 Java Streams API 將 FunTester 物件按司齡(getCompanyYears
)進行分組。以下是完整示例,展示瞭如何按工作年限對 FunTester 物件進行分組,並輸出分組結果。
進階
Java Streams 甚至支援巢狀分組。例如可以先根據司齡進行分組,然後再根據實際年齡分組。
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
Map<Integer, Map<String, List<FunTester>>> collect = testers.stream()
.collect(Collectors.groupingBy(FunTester::getCompanyYears, Collectors.groupingBy(t -> {
if (t.getAge() > 35) {// 年齡大於35歲,則分組
return "年齡大";
} else {
return "年齡小";
}
})));
}
透過巢狀的 groupingBy
,你可以將資料進行多層次的分組,使得組織資料更為靈活。
Java Streams 的潛力
Java Streams 的強大功能遠不止於此。隨著你深入探索這個 API 的更多特性,你會發現它不僅能幫助你處理集合資料,還能極大地提升程式碼的可讀性和維護性。透過不斷實踐和應用,你將逐漸掌握這些工具的精髓,使得程式碼變得更加高效、簡潔、優雅。不論是處理複雜的資料轉換,還是實現更靈活的操作,Java Streams 都能為你提供無窮的可能性,讓你的程式設計技巧更上一層樓。
FunTester 原創精華
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go
- 白盒、工具、爬蟲、UI 自動化
- 理論、感悟、影片