JDK都出到20多了,你還不會使用JDK8的Stream流寫代碼嗎?

目錄

前言

Stream流 是什么?

為什么要用Steam流

常見stream流使用案例

映射 map()? & 集合 collect()

單字段映射

多字段映射

映射為其他的對象

映射為 Map

去重 distinct()

過濾 filter()

Stream流的其他方法

使用Stream流的弊端


前言

當你某天看到舍友的代碼不再寫for循環時,你的反應:

你還在 new Collection<>() ,寫著for循環的時候,舍友已經開始偷偷卷你,更改代碼風格了

本文將帶著大家簡單理解 Stream 流,并通過部分案例描述 Stream 流?的實用方法

Stream流 是什么?

Stream 流Java 8 引入的一個強大工具,它提供了一種全新的方式來處理集合和數組等數據源,使得數據處理變得更加簡單、高效和易于理解。


通俗的理解起來就是提供了一種更加便利的遍歷處理方式。


如果你要問我 Stream流用起來什么感覺?

那我只能說,,這種感覺就像飛翔在~~

噢不對,,感覺就是:

為什么要用Steam流

?Stream 流的主要用途是提供一種高效且表達力高的方式來處理集合和數組等數據源。通過使用 Stream 流,可以避免顯式的迭代器和循環,使得代碼更加簡潔、易讀。Stream 流支持復雜的查詢/過濾、映射/轉換、歸約/匯總等操作,能夠極大地簡化數據處理的復雜度。

總結起來還是:簡潔、易讀

當然,這也讓你的代碼看起來更高級那么一點點~~

如下案例,拿到所有的評論的id 集合的兩種方法。

  • 第一種-for循環便利獲取
		List<Comment> list = commentMapper.selectList(wrapper);List<Integer> commentId = new ArrayList<>();for(Comment c : list){commentId.add(c.getId());}
  • 第二種-Stream流獲取
		List<Comment> list = commentMapper.selectList(wrapper);List<Integer> commentId = list.stream().map(Comment::getId).collect(Collectors.toList());

兩種方法的區別顯而易見

下面介紹stream流比較實用的方法

常見stream流使用案例

在這里我們準備一個簡單的對象來進行案例測試,只約定兩個字段。

@Data
@AllArgsConstructor
public class StreamTestObject {Integer id1;Integer id2;}

映射 map()? & 集合 collect()

map() 方法是最常用的方法之一,它可以將流中的每個元素轉換成另一種形式,返回轉換后的 Stream

如前文的例子所示,

單字段映射
        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(5, 6));// 便于觀察變化Stream<StreamTestObject> stream = streamTestObjects.stream();Stream<Integer> id1Stream = streamTestObjects.stream().map(StreamTestObject::getId1);

看代碼我們可以看到,map方法將對象的stream流映射為了其中? id1 這個字段的stream流


拿到這個字段的流后,可以做些什么呢?

最常用的方法之一就是與 集合 collect() 搭配起來使用。

那么 collect() 方法能做寫什么呢?

  • 用途:將流中的元素累積成一個匯總結果,我們可以按照自己的需求將結果匯總為一個 List、Set、Map 等

如下代碼所示

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(5, 6));Stream<StreamTestObject> stream = streamTestObjects.stream();Stream<Integer> id1Stream = streamTestObjects.stream().map(StreamTestObject::getId1);List<Integer> collectList = id1Stream.collect(Collectors.toList());
//        Set<Integer> collectSet = id1Stream.collect(Collectors.toSet());// 連起來使用一行代碼可以寫成這樣collectList = streamTestObjects.stream().map(StreamTestObject::getId1).collect(Collectors.toList());
//        collectSet = streamTestObjects.stream().map(StreamTestObject::getId1).collect(Collectors.toSet());System.out.println("collectList:" + collectList);
// 輸出結果 collectList:[1, 3, 5]

結果能夠把 id1 成功收集起來,代碼的易讀性也體現在其中。我們一眼就能看出這行代碼映射了id1 這個字段為一個 List Set

多字段映射

那如果我們想要對象集合中的 id1 和 id2 都匯總到一個 List<Integer> 集合里,應該如何操作呢。

這里我們可以使用一個 flatMap() 方法

  • 用途:將流中的每個元素都轉換成另一個流,然后將所有流連接成一個流。

直接上代碼

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(5, 6));List<Integer> collectList = streamTestObjects.stream().flatMap(object ->Stream.of(object.getId1(), object.getId2())).collect(Collectors.toList());System.out.println("collectList:" + collectList);//輸出結果 collectList:[1, 2, 3, 4, 5, 6]

在這個例子中,Stream.of(obj.getId1(), obj.getId2())為每個對象生成了一個包含兩個ID的流,它在map中 形成了一個臨時的流

然后flatMap將這些流“展平”成了一個包含所有ID的流,最后我們通過collect(Collectors.toList())將這個流收集到了一個列表中。

映射為其他的對象

有的時候的業務需求需要我們把一個對象集合轉化為另外一個集合對象,如果是單純的 字段copy,我們可以使用 BeanUtils?或者 MapStruct 等方法實現。

如果轉化的過程中設計業務邏輯,那么就需要 Stream流出手了。

這里需要設計到一個顯示 return 的寫法,上代碼,先準備一個另外的對象,只包含一個id字段

@Data
@NoArgsConstructor
@AllArgsConstructor
public class StreamOtherObject {Integer id;}

然后 我們將上述測試對象的id1,轉化為這里的id字段。

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<StreamOtherObject> collectList = streamTestObjects.stream().map(streamTestObject -> {StreamOtherObject object = new StreamOtherObject();object.setId(streamTestObject.getId1());return object;}).collect(Collectors.toList());System.out.println("collectList:" + collectList);//輸出結果 collectList:[StreamOtherObject(id=1), StreamOtherObject(id=3), StreamOtherObject(id=4)]

注意看返回的集合對象已經是我在表達式里return 的 StreamOtherObject了。

也就是 return 的內容就是集合的具體對象

映射為 Map

Stream流還能把集合映射為一個Map,這里我們測試用例為將映射結果設置為 key 為 id1, value 為對象本身

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4));Map<Integer, StreamTestObject> map = streamTestObjects.stream().collect(Collectors.toMap(StreamTestObject::getId1,Function.identity(),(existsOne, replaceOne) -> replaceOne));System.out.println("collectMap:" + map);//輸出結果 collectMap:{1=StreamTestObject(id1=1, id2=2), 3=StreamTestObject(id1=3, id2=4)}

可以看到,toMap() 方法中,傳遞了3個參數,前兩個分別為 key value

第三個參數傳了一個表達式,這里的邏輯表示如果發生沖突,就保留 Map中新的那個對象,而不是保留它。同時,第三個參數也處理了沖突,如果你沒有對于 key 相同的情況做處理,也就是 key 沖突了,方法將拋出一個IllegalStateException

所以你需要做對應的處理,如try catch 下來,或者進行沖突處理,即傳遞第三個參數。

去重 distinct()

對于拿到的流結果,我們有的時候有去重的需求,當然我們可以轉為 toSet()?進行去重

stream流同樣提供了一個方法進行去重,就是 distinct() 方法

  • 用途:去除流中的重復元素,返回包含不同元素的 Stream

這里比較好理解,我們直接看案例

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<Integer> collectList = streamTestObjects.stream().flatMap(object ->Stream.of(object.getId1(), object.getId2())).distinct().collect(Collectors.toList());System.out.println("collectList:" + collectList);//輸出結果 collectList:[1, 2, 3, 4, 5]

過濾 filter()
  • 用途:根據提供的條件過濾元素,返回滿足條件的 Stream。

過濾的方法也是比較常用的方法,也是比較多業務中有這個需求的。這里介紹兩種方法

在拿到一個流后,也許不是所有的元素我們都需要。我們需要保存滿足特定條件的元素,這時候就可以使用 filter方法來實現。

這里的案例表示篩選除 id1 為 1,id2 為 4 的數據,代碼如下:

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<StreamTestObject> collectList = streamTestObjects.stream().filter(object -> object.getId1().equals(1) || object.getId2().equals(4)).collect(Collectors.toList());System.out.println("collectList:" + collectList);//輸出結果 collectList:[StreamTestObject(id1=1, id2=2), StreamTestObject(id1=3, id2=4)]

如果你的過濾邏輯比較復雜可以使用顯示 return 寫法來過濾

        List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<StreamTestObject> collectList = streamTestObjects.stream().filter(object -> {int id1 = 1;int id2 = 4;return object.getId1().equals(id1) || object.getId2().equals(id2);}).collect(Collectors.toList());System.out.println("collectList:" + collectList);//輸出結果 collectList:[StreamTestObject(id1=1, id2=2), StreamTestObject(id1=3, id2=4)]

在代碼塊里可以編輯自己自定義的過濾邏輯

這里要注意返回值是一個布爾值,如果為 true,則保留這項數據,不滿足,則進行一項數據處理。

Stream流的其他方法

前文是 Stream 流比較常見的方法案例,它還提供了很多其他的接口來實現對應的場景,如:

  1. sorted()
    • 用途:對流中的元素進行自然排序(需實現 Comparable 接口),返回排序后的 Stream。
    • 示例:對用戶列表按年齡進行排序。
  2. limit(long maxSize)
    • 用途:截斷流,使其包含不超過給定數量的元素,返回截斷后的 Stream。
    • 示例:只取用戶列表中的前三個用戶。
  3. skip(long n)
    • 用途:跳過流中的前 n 個元素,返回剩下的元素的 Stream。
    • 示例:跳過用戶列表中的前兩個用戶,取后面的用戶。
  4. forEach(Consumer<? super T> action)
    • 用途:這是大家比較熟悉的操作,在代碼編寫中可以省去 .Stream() 的寫法。意為對流中的每個元素執行提供的操作,這是一個終結操作。
    • 示例:遍歷用戶列表并打印每個用戶的名字。

使用Stream流的弊端

學習了Stream流 的優點之后,也需要知道隨之產生的弊端有短些,這里我列舉幾個主要的內容

  • 性能問題
  1. 多次遍歷:有時為了完成一個操作,可能需要多次遍歷數據源。例如,先過濾(filter)再映射(map)最后收集(collect),這會導致數據被多次遍歷。
  2. 并行流開銷:雖然并行流可以加速處理過程,但它們引入了額外的線程管理開銷,并且不總是能帶來性能提升,尤其是在數據源較小或操作相對簡單時。
  3. 懶加載導致的意外行為:Stream操作是懶加載的,這意味著它們直到需要結果時才會執行。這可能導致在調試時難以追蹤問題的源頭,或者在某些情況下,當流操作依賴于外部狀態時,可能導致不可預測的行為。

  • 可讀性和維護性

??:前面不是可讀性強嗎,怎么有問題了?如果嵌套太多層的操作方法,也會使得表達式的可讀性降低

  1. 復雜邏輯難以追蹤:對于包含多個復雜操作(如多重過濾、映射、歸約等)的Stream鏈,其邏輯可能變得難以理解和追蹤。
  2. 調試困難:由于Stream操作的延遲執行和中間操作的無狀態性,調試Stream代碼可能會比傳統循環更加困難。

  • 錯誤處理
  1. 異常處理復雜:在Stream操作中處理異常(如嘗試映射一個可能拋出異常的函數)比在傳統循環中更復雜。Stream API沒有直接支持異常處理機制,通常需要通過try-catch塊或自定義函數來處理。

  • 內存消耗
  1. 中間結果存儲:Stream API在執行過程中可能會創建中間結果的臨時集合,尤其是在進行復雜操作時,這可能會增加內存消耗。

到這里,同學們可以多實操一下這些方法來鞏固知識。文章如有遺漏或建議更改的部分歡迎佬們指出。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40917.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40917.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40917.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于深度學習LightWeight的人體姿態檢測跌倒系統源碼

一. LightWeight概述 light weight openpose是openpose的簡化版本&#xff0c;使用了openpose的大體流程。 Light weight openpose和openpose的區別是&#xff1a; a 前者使用的是Mobilenet V1&#xff08;到conv5_5&#xff09;&#xff0c;后者使用的是Vgg19&#xff08;前10…

公務員考試、事業編考試、教師資格證、面試、K12資料、電子書

點擊上方△騰陽 關注 作者 l 騰陽 轉載請聯系授權 你好&#xff0c;我是騰陽。 在這個自媒體的海洋里&#xff0c;我曾是一只迷失方向的小鳥&#xff0c;多次嘗試飛翔卻總是跌跌撞撞。 但每一次跌倒&#xff0c;都讓我更堅定地相信&#xff0c;只要不放棄&#xff0c;總…

【Unity2D 2022:Particle System】添加命中粒子特效

一、創建粒子特效游戲物體 二、修改粒子系統屬性 1. 基礎屬性 &#xff08;1&#xff09;修改發射粒子持續時間&#xff08;Duration&#xff09;為1s &#xff08;2&#xff09;取消勾選循環&#xff08;Looping&#xff09; &#xff08;2&#xff09;修改粒子存在時間&…

2024全網最全面及最新且最為詳細的網絡安全技巧五 之 SSRF 漏洞EXP技巧,典例分析以及 如何修復 (上冊)———— 作者:LJS

五——SSRF漏洞 EXP技巧&#xff0c;典例分析以及 如何修復 目錄 五——SSRF EXP技巧&#xff0c;典例分析以及 如何修復 5.1Apache mod_proxy SSRF&#xff08;CVE-2021-40438&#xff09;的一點分析和延伸 0x01 Apache Module綜述 0x02 漏洞原理分析 Apache在配置反代的后端…

Vue的學習之生命周期

一、生命周期 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>Vue的學習</title><script src"vue.js" type"text/javascript" charset"utf-8"></script></head>&l…

C#如何從中級進階到高級開發

從中級C#開發進階到高級開發&#xff0c;需要深入理解和掌握更復雜的技術和架構&#xff0c;同時培養解決問題的能力和創新思維。以下是一些關鍵的技能和步驟&#xff0c;可以幫助你從中級向高級開發邁進&#xff1a; 1. 深入理解C#語言特性 泛型&#xff1a;熟練使用泛型提高…

Java實現登錄驗證 -- JWT令牌實現

目錄 1.實現登錄驗證的引出原因 2.JWT令牌2.1 使用JWT令牌時2.2 令牌的組成 3. JWT令牌&#xff08;token&#xff09;生成和校驗3.1 引入JWT令牌的依賴3.2 使用Jar包中提供的API來實現JWT令牌的生成和校驗3.3 使用JWT令牌驗證登錄3.4 令牌的優缺點 1.實現登錄驗證的引出 傳統…

強化Linux系統安全性:從基礎命令到高級管理

強化Linux系統安全性&#xff1a;從基礎命令到高級管理 引言 在網絡安全領域&#xff0c;Linux系統因其穩定性和安全性而廣受歡迎。作為一名網絡安全專家&#xff0c;我將分享如何通過Linux基礎命令和高級管理技巧來加強系統的安全性。本文將基于《學神 IT 教育》提供的Linux…

Debezium報錯處理系列之第110篇: ERROR Error during binlog processing.Access denied

Debezium報錯處理系列之第110篇:ERROR Error during binlog processing. Last offset stored = null, binlog reader near position = /4 Access denied; you need at least one of the REPLICATION SLAVE privilege for this operation 一、完整報錯二、錯誤原因三、解決方法…

python 切入點(EntryPoints)使用

文章目錄 EntryPoints 介紹EntryPoints案例EntryPoints 介紹 官網參考 EntryPoints 是發布的python 項目的一種機制,可以提供對自身項目的切入點,供其他項目代碼使用。在python環境中可以通過importlib.metadata.entry_points 函數發現所有的切入點插件,并在代碼中加載、調…

08_排序

基本概念與分類 假設含有n個記錄的序列為 { r 1 , r 2 , . . . , r n } \{r_1,r_2,...,r_n\} {r1?,r2?,...,rn?}&#xff0c;其相應的關鍵字分別為 { k 1 , k 2 , . . . , k n } \{k_1,k_2,...,k_n\} {k1?,k2?,...,kn?}&#xff0c;需確定1&#xff0c;2&#xff0c;…&…

微服務: Nacos部署安裝與properties配置

Nacos 是阿里巴巴開源的一款用于動態服務發現、配置管理和服務管理的基礎設施。Nacos 這個名稱源自于 “Dynamic Naming and Configuration Service”。它主要是用于解決微服務架構中服務發現和配置管理的問題。 Nacos 單機模式的部署安裝 1. 安裝(Windows環境) Nacos是Java…

Java線程基礎知識總結

基礎概念 Java 線程是并發編程的基礎&#xff0c;涉及到線程的創建、管理、同步以及通信。理解和掌握線程的使用對于編寫高效和響應快速的應用程序至關重要。 1. 線程基礎 線程是程序中的執行流。每個Java程序至少有一個線程 — 主線程&#xff08;main&#xff09;。通過使…

從入門到深入,Docker新手學習教程

編譯整理&#xff5c;TesterHome社區 作者&#xff5c;Ishaan Gupta 以下為作者觀點&#xff1a; Docker 徹底改變了我們開發、交付和運行應用程序的方式。它使開發人員能夠將應用程序打包到容器中 - 標準化的可執行組件&#xff0c;將應用程序源代碼與在任何環境中運行該代碼…

InspireFace-商用級的跨平臺開源人臉分析SDK

InspireFace-商用級的跨平臺開源人臉分析SDK InspireFaceSDK是由insightface開發的?款?臉識別軟件開發?具包&#xff08;SDK&#xff09;。它提供了?系列功能&#xff0c;可以滿?各種應?場景下的?臉識別需求&#xff0c;包括但不限于閘機、?臉?禁、?臉驗證等。 該S…

ubuntu22 sshd設置

專欄總目錄 一、安裝sshd服務 sudo apt updatesudo apt install -y openssh-server 二、配置sshd 使用文本編輯器打開/etc/ssh/sshd_config sudo vi /etc/ssh/sshd_config &#xff08;一&#xff09;配置sshd服務的偵聽端口 建議將ssh的偵聽端口改為7000以上的端口&#…

【bazel】快速下載教程

bazel下載鏈接&#xff1a; https://github.com/bazelbuild/bazel/releases?page11 直接在github上下載&#xff0c;會因為網絡不穩定&#xff0c;而頻繁下載錯誤 這里提供一個超級快速的方法&#xff01;&#xff01;&#xff01; 用迅雷下載&#xff01; 1.從github上復…

cpp http server/client

httplib 使用httplib庫 basedemo server.cpp #include "httplib.h" #include <iostream> using namespace httplib;int main(void) {Server svr;svr.Get("/hello", [](const Request& req, Response& res) {std::cout << "lo…

實現Java Web應用的高性能負載均衡方案

實現Java Web應用的高性能負載均衡方案 大家好&#xff0c;我是微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 在高并發的網絡環境中&#xff0c;負載均衡是確保Web應用程序高性能和可靠性的關鍵策略之一。本文將探討如何…

【力扣 - 每日一題】3115. 質數的最大距離(一次遍歷、頭尾遍歷、空間換時間、埃式篩、歐拉篩、打表)Golang實現

原題鏈接 題目描述 給你一個整數數組 nums。 返回兩個&#xff08;不一定不同的&#xff09;質數在 nums 中 下標 的 最大距離。 示例 1&#xff1a; 輸入&#xff1a; nums [4,2,9,5,3] 輸出&#xff1a; 3 解釋&#xff1a; nums[1]、nums[3] 和 nums[4] 是質數。因此答…