JavaRush /Java Blog /Random-TW /喝咖啡休息#177。Java 8 中的 Java Stream 詳細指南

喝咖啡休息#177。Java 8 中的 Java Stream 詳細指南

在 Random-TW 群組發布
來源:Hackernoon 這篇文章提供了使用 Java Stream 的詳細教學課程以及程式碼範例和解釋。 喝咖啡休息#177。 Java 8 中的 Java Stream 詳細指南 - 1

Java 8 中的 Java 執行緒簡介

Java Streams 作為 Java 8 的一部分引入,用於處理資料集合。它們本身不是資料結構,但可用於透過排序和管線操作從其他資料結構輸入資訊以產生最終結果。 注意:不要混淆 Stream 和 Thread,這一點很重要,因為在俄語中,這兩個術語通常在同一個翻譯中被稱為「流」。Stream 表示用於執行操作(最常見的是傳輸或儲存資料)的對象,而 Thread(字面翻譯 - 執行緒)表示允許您與其他程式碼分支並行執行某些程式碼的對象。 因為 Stream 不是一個單獨的資料結構,所以它永遠不會改變資料來源。Java流具有以下特點:
  1. Java Stream 可以透過「java.util.stream」套件來使用。可以使用以下程式碼將其匯入到腳本中:

    import java.util.stream.* ;

    使用這段程式碼,我們還可以輕鬆實作Java Stream中的幾個內建函數。

  2. Java Stream可以接受來自資料集合的輸入,例如Java中的集合和陣列。

  3. Java Stream 不需要更改輸入資料結構。

  4. Java Stream 不會更改來源。相反,它使用適當的管道方法來產生輸出。

  5. Java Streams 受中間和終端操作的影響,我們將在以下部分中討論。

  6. 在 Java Stream 中,中間操作是管道化的,並以惰性求值格式發生。它們以終端函數結束。這構成了使用 Java Stream 的基本格式。

在下一節中,我們將了解 Java 8 中用於在需要時建立 Java Stream 的各種方法。

在 Java 8 中建立 Java 流

Java 執行緒可以透過多種方式建立:

1.使用Stream.empty()方法建立一個空流

您可以建立一個空流以供稍後在程式碼中使用。如果使用Stream.empty()方法,將產生一個空流,不包含任何值。如果我們想在運行時跳過空指標異常,這個空流會派上用場。為此,您可以使用以下命令:
Stream<String> str = Stream.empty();
上面的語句將產生一個名為str的空流,其中不包含任何元素。要驗證這一點,只需使用術語str.count()檢查流的數量或大小。例如,
System.out.println(str.count());
結果,我們在輸出處得到0

2. 使用 Stream.builder() 方法和 Stream.Builder 實例建立流

我們也可以使用Stream Builder使用建構器設計模式建立流。它是為逐步建立物件而設計的。讓我們看看如何使用Stream Builder實例化流。
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
使用此程式碼,您可以建立一個名為numStream的包含int elements 的流。由於首先創建了 名為numBuilder的Stream.Builder實例,一切都很快完成。

3.使用Stream.of()方法建立具有指定值的流

創建流的另一種方法涉及使用Stream.of()方法。這是創建具有給定值的流的簡單方法。它聲明並初始化線程。使用Stream.of()方法建立流的範例:
Stream<Integer> numStream = Stream.of(1, 2, 3);
此程式碼將建立一個包含int元素 的流,就像我們在前面使用Stream.Builder 的方法中所做的那樣。這裡我們直接使用Stream.of()創建了一個具有預先定義值[1, 2 和 3]的流。

4. 使用 Arrays.stream() 方法從現有陣列建立流

建立執行緒的另一種常見方法是在 Java 中使用陣列。這裡的流是使用Arrays.stream()方法從現有陣列建立的。所有數組元素都轉換為流元素。這是一個很好的例子:
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
此程式碼將產生一個numStream,其中包含名為 arr 的陣列的內容,該陣列是一個整數陣列。

5. 使用 Stream.concat() 方法合併兩個現有流

另一種可用於建立流的方法是Stream.concat()方法。它用於組合兩個線程以創建單個線程。兩個流依序組合。這意味著第一個線程先出現,然後是第二個線程,依此類推。此類串聯的範例如下所示:
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
上面的語句將建立一個名為combinedStream的最終流,其中逐一包含第一個流numStream1和第二個流numStream2的元素。

Java Stream 的操作類型

如前所述,您可以在 Java 8 中使用 Java Stream 執行兩種類型的操作:中間操作和終端操作。讓我們更詳細地看看它們中的每一個。

中間操作

中間操作產生輸出流,並且僅在遇到終端操作時才執行。這意味著中間操作是延遲執行的、管道化的,並且只能由終端操作完成。稍後您將了解惰性求值和管道。中間運算的範例有以下方法:filter()map()difference()peek()sorted()等。

終端操作

終端操作完成中間操作的執行,同時返回輸出流的最終結果。由於終端操作標誌著延遲執行和管線操作的結束,因此該執行緒在經歷終端操作後不能再次使用。終端操作的範例有以下方法:forEach()collect()count()reduce()等。

Java Stream 的操作範例

中間操作

以下是一些可應用於 Java Stream 的中間操作的範例:

篩選()

此方法用於過濾流中與 Java 中的特定謂詞相符的元素。然後,這些過濾的項目組成一個新的流。讓我們看一個例子來更好地理解。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
結論:
[98]
說明:在此範例中,您可以看到,即使是元素(可被 2 整除)也會使用filter()方法 進行過濾,並儲存在整數列表numStream中,稍後會列印該清單的內容。由於 98 是流中唯一的偶數,因此它會印在輸出中。

地圖()

此方法用於透過對原始輸入流的元素執行映射函數來建立新流。也許新流具有不同的資料類型。此範例如下圖所示:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
結論:
[86、130、2、196、126]
說明:在這裡我們看到map()方法用於簡單地將numStream流的每個元素加倍。從輸出中可以看到,流中的每個元素都已成功加倍。

清楚的()

此方法用於透過過濾掉重複項來僅檢索流中的單一元素。相同的範例如下所示:
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
結論:
[43、65、1、98、63]
說明:在這種情況下,different()方法用於numStream它透過從流中刪除重複項來檢索 numList 中的所有單一元素。正如您從輸出中看到的,沒有重複項,這與輸入流不同,輸入流最初有兩個重複項(63 和 1)。

窺視()

此方法用於在執行終端操作之前追蹤中間變更。這意味著peek()可用於對流的每個元素執行操作,以建立可以執行進一步中間操作的流。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
結論:
映射:430 映射:650 映射:10 映射:980 映射:630 [430、650、10、980、630]
說明:這裡peek()方法用來產生中間結果,因為map()方法應用於流的元素。這裡我們可以注意到,即使在使用collect()終端操作在列印語句中列印清單的最終內容之前,每個流元素映射的結果也會事先依序列印。

排序()

Sorted()方法用於對流的元素進行排序。預設情況下,它會按升序對元素進行排序。您也可以指定特定的排序順序作為參數。此方法的範例實作如下所示:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
結論:
1 43​​ 63 65 98
說明:這裡,sorted()方法用於預設按升序對流中的元素進行排序(因為沒有指定特定順序)。您可以看到輸出中列印的項目按升序排列。

終端操作

以下是一些可應用於 Java 流的終端機操作的範例:

forEach()

forEach()方法用於迭代流的所有元素,並對每個元素一一執行函數。這可以作為迴圈語句(例如forwhile等)的替代語句。例子:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
結論:
43 65 1 98 63
說明:這裡的forEach()方法用於逐一列印流中的每個元素。

數數()

count()方法用於檢索流程中存在的元素總數。它類似於size()方法,通常用於確定集合中元素的總數。將count()方法與 Java Stream 一起使用的範例如下:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
結論:
5
說明:由於numStream包含 5 個整數元素,因此對其使用count()方法將輸出 5。

收集()

collect()方法用於執行流元素的可變縮減。它可用於在處理完成後從串流中刪除內容。它使用Collector類別來執行縮減。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
結論:
[43, 65, 1, 63]
說明:在此範例中,流中的所有奇數元素都被過濾並收集/減少到名為odd 的清單中。最後,列印出奇數列表。

最小值()最大值()

min()方法,顧名思義,可用於流中找出其中的最小元素。類似地,max()方法可用來尋找流中的最大元素。讓我們嘗試透過範例來了解如何使用它們:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
結論:
最小元素:1 最大元素:98
說明:在本例中,我們使用min ()方法 列印 numStream 中的最小元素,並使用max()方法列印最大元素。注意這裡,在使用max()方法之前,我們已經再次在numStream中加入了元素。這是因為min()是終端操作,會破壞原始流的內容,只回傳最終結果(在本例中是整數「最小」)。

findAny()findFirst()

findAny()傳回流中的任何元素作為Optional。如果流為空,它還將傳回一個可選值,該值將為空。 findFirst()將流的第一個元素作為Optional傳回。與findAny()方法一樣,如果對應的流為空,則findFirst()方法也會傳回一個空的可選參數。讓我們來看看下面基於這些方法的範例:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
結論:
可選[43] 可選.空
說明:這裡,在第一種情況下,findFirst()方法將流的第一個元素作為Optional傳回。然後,當線程被重新分配為空線程時,findAny()方法會傳回一個空的Optional

allMatch()anyMatch()noneMatch()

allMatch()方法用於檢查流中的所有元素是否符合某個謂詞,如果匹配則傳回布林值true ,否則傳回false。如果流為空,則傳回trueanyMatch()方法用於檢查流中的任何元素是否與某個謂詞相符。如果是則回傳true,否則傳回 false。如果流為空,則傳回false。如果流中沒有元素與謂詞匹配,則noneMatch()方法傳回true ,否則傳回false。說明這一點的範例如下所示:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
結論:
假 真 假
說明:對於包含 1 作為元素的 流numStream , allMatch()方法傳回false,因為所有元素都不為 1,但只有一個元素為 1。anyMatch()方法傳回true,因為至少有一個元素為 1。noneMatch ()方法傳回false,因為 1 實際上是作為該流中的一個元素存在。

Java Stream 中的惰性求值

在 Java 8 中使用 Java Streams 時,惰性求值會帶來最佳化。它們主要涉及延遲中間操作,直到遇到終端操作。惰性求值負責防止計算上不必要的資源浪費,直到實際需要結果為止。中間操作產生的輸出流僅在終端操作完成後產生。惰性求值適用於 Java 流程中的所有中間操作。當處理無限流時,惰性求值的一個非常有用的用途。這樣就可以避免很多不必要的處理。

Java Stream 中的管道

Java Stream 中的管道由一個輸入流、零個或多個依序排列的中間操作以及最後一個終端操作組成。Java Streams 中的中間操作是惰性執行的。這使得管線中間操作不可避免。使用管道(基本上是按順序組合的中間操作),延遲執行成為可能。管道有助於追蹤最終遇到終端操作後需要執行的中間操作。

結論

現在讓我們總結一下今天所學到的內容。在本文中:
  1. 我們快速了解了 Java Streams 是什麼。
  2. 然後我們學習了許多在 Java 8 中建立 Java 執行緒的不同技術。
  3. 我們學習了可以在 Java 流上執行的兩種主要操作類型(中間操作和終端操作)。
  4. 然後我們詳細研究了中間和終端操作的幾個範例。
  5. 我們最終了解了有關延遲計算的更多信息,並最終了解了 Java 線程中的管道操作。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION