Java 8引入的Stream API是一個功能強大、靈活且可組合的流處理模式,它允許我們以一種比傳統的for循環更簡潔、更易於閱讀和管理的方式處理數據。Stream API基於Lambda表達式和方法引用實現了一種函數式編程樣式,可以將我們的程序從特定的實現中解耦出來。本篇文章將介紹Java Stream的用法和特性,旨在幫助讀者更好地掌握和運用Java Stream。
一、生成Stream
Stream可以從數組、集合、文件、網路、任何對象等多種數據源生成,我們可以使用Stream的靜態方法創建Stream、或者將已有的Stream處理成新的Stream,這些靜態方法包括:
Stream.of(T...) Stream.empty() Arrays.stream(T[]) Collection.stream()
其中,Stream.of()用於將數組或任意個數的元素轉換為Stream;Stream.empty()用於生成空的Stream;Arrays.stream()用於將數組轉換為Stream;Collection.stream()用於將Collection轉換為Stream。
二、Stream的操作
1. 中間操作
中間操作是指返回Stream類型的操作,可以被其他Stream操作鏈式調用,例如filter()、map()、sorted()、distinct()等。這些操作可以串接使用,我們可以在一個Stream上執行多個中間操作,以構建出一個複雜邏輯的數據流處理鏈。
1.1 filter()
filter()方法用於排除不符合條件的元素,該方法需要傳入一個Predicate類型的Lambda表達式,該表達式接受一個參數,並返回一個boolean類型的結果,如果結果為true,則保留該元素;如果為false,則排除該元素。例如,篩選出一個整數集合中所有大於等於5的元素:
IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) .filter(num -> num >= 5) .forEach(System.out::println);
該代碼會輸出:
5 6 7 8 9 10
1.2 map()
map()方法用於將一個元素轉換為另一個類型的元素,它需要傳入一個Function類型的Lambda表達式,該表達式接受一個參數,並返回一個處理後的結果。例如,將一個字元串集合中所有的字元串轉換為大寫格式:
List list = Arrays.asList("apple", "banana", "orange"); list.stream() .map(String::toUpperCase) .forEach(System.out::println);
該代碼會輸出:
APPLE BANANA ORANGE
1.3 flatMap()
flatMap()方法可以將一個Stream中的每個對象分解為多個Stream,最後將多個Stream合成一個Stream並返回。flatMap()方法需要傳入一個Function類型的Lambda表達式,該表達式接受一個參數,並返回一個Stream類型的結果。例如,取出一個集合中所有字元串的單詞,並將其輸出:
List list = Arrays.asList("Hello World", "Stream API"); list.stream() .flatMap(str -> Stream.of(str.split(" "))) .forEach(System.out::println);
該代碼會輸出:
Hello World Stream API
1.4 distinct()
distinct()方法用於去重,它返回一個去除了重複元素的新Stream。如果集合元素沒有重寫equals()和hashCode()方法,則該方法只能使用在基本類型Stream上。例如,去除一組數字中的重複元素:
IntStream.of(1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8) .distinct() .forEach(System.out::println);
該代碼會輸出:
1 2 3 4 5 6 7 8
1.5 sorted()
sorted()方法用於對Stream中的元素進行排序,排序時可以自定義排序規則,排序最終會返回一個新的Stream。例如,對一組數字進行排序:
IntStream.of(4, 2, 5, 1, 6, 3) .sorted() .forEach(System.out::println);
該代碼會輸出:
1 2 3 4 5 6
2. 終止操作
終止操作是指返回非Stream類型的操作,它們將Stream管道處理後的結果輸出或返回。在一個Stream管道中,只有一個終止操作才會觸發流動處理,終止操作是Stream管道處理的必要環節。終止操作包括:
- forEach()
- count()
- findFirst()
- reduce()
- toArray()
- collect()
2.1 forEach()
forEach()方法用於遍歷Stream中的元素,它會接收一個Consumer類型的Lambda表達式作為參數,該表達式接受一個參數,並沒有返回值。例如,遍歷一個整數集合併輸出其平方:
IntStream.of(1, 2, 3, 4, 5) .forEach(num -> System.out.println(num * num));
該代碼會輸出:
1 4 9 16 25
2.2 count()
count()方法用於計算Stream中元素的個數,返回一個long類型的結果。例如,計算一組數字的個數:
long count = IntStream.of(1, 2, 3, 4, 5).count(); System.out.println("count: " + count);
該代碼將輸出:
count: 5
2.3 findFirst()
findFirst()方法用於查找Stream中第一個符合條件的元素,返回一個Optional類型的結果。例如,查找一組字元串中第一個以大寫字母開頭的元素:
List list = Arrays.asList("a", "b", "C", "d"); Optional firstUpper = list.stream().filter(str -> Character.isUpperCase(str.charAt(0))).findFirst(); System.out.println("first upper: " + firstUpper.orElse("not found"));
該代碼將輸出:
first upper: C
2.4 reduce()
reduce()方法用於對Stream中的所有元素進行「累加」處理,並返回處理結果。reduce()方法需要傳入兩個參數,第一個參數是初始值,第二個參數是一個BinaryOperator類型的Lambda表達式,該表達式接收兩個同類型的參數,並返回一個同類型的結果。例如,計算一組數字的和:
int sum = IntStream.of(1, 2, 3, 4, 5).reduce(0, (a, b) -> a + b); System.out.println("sum: " + sum);
該代碼將輸出:
sum: 15
2.5 toArray()
toArray()方法用於將Stream生成一個數組,返回一個T[]類型的結果。例如,將集合中所有的元素保存到數組中:
List list = Arrays.asList("a", "b", "c"); String[] arr = list.stream().toArray(String[]::new); System.out.println("array: " + Arrays.toString(arr));
該代碼將輸出:
array: [a, b, c]
2.6 collect()
collect()方法是非常重要的一個操作,它可以將Stream中的所有元素按照指定的方式收集起來,可以選取任何類型的集合、Map、數組,或者自定義的Collector進行收集操作。例如,將一組字元串拼接為一個字元串:
List list = Arrays.asList("a", "b", "c"); String str = list.stream().collect(Collectors.joining("-")); System.out.println("merged string: " + str);
該代碼將輸出:
merged string: a-b-c
三、並行處理與性能
Stream API支持並行處理,可以讓程序利用多核CPU的優勢,提高數據處理性能。我們可以通過parallel()方法將一個Stream切換為並行流,或者通過sequential()方法將並行流切換為順序流。例如,計算一組整數的平方和:
long start = System.currentTimeMillis(); int result = IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) .parallel() // 切換為並行流 .map(num -> num * num) .reduce(0, (a, b) -> a + b); long end = System.currentTimeMillis(); System.out.println("result: " + result); System.out.println("time used: " + (end - start) + "ms");
該代碼將輸出:
result: 385 time used: 11ms
當然,並不是所有情況都適合使用並行流,可能會存在以下問題:
- 數據量較小:並行流的創建和銷毀需要一定的時間,當數據量很小時,順序流可能會更快。
- 有些操作不適合併行處理:例如排序和去重。
- 有些操作需要先完成一部分才能進行下一步操作:例如findFirst()。
四、結束語
Java Stream API是Java 8引入的重要特性之一,雖然Stream看起來很複雜,但實際上它是與函數式編程思想一脈相承的,它可以幫助我們更好地編寫與數據處理相關的代碼。本文介紹了Stream的基本用法、操作類型和並行處理特性,希望能夠幫助讀者更好地掌握和運用Java Stream API。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/303781.html