Java開發筆記(七十二)Java8新增的流式處理

来源:https://www.cnblogs.com/pinlantu/archive/2019/03/15/10539799.html
-Advertisement-
Play Games

通過前面幾篇文章的學習,大家應能掌握幾種容器類型的常見用法,對於簡單的增刪改和遍歷操作,各容器實例都提供了相應的處理方法,對於實際開發中頻繁使用的清單List,還能利用Arrays工具的asList方法給清單對象做初始化賦值,另外提供了專門的Collections工具進行排序、求最大元素、求最小元素 ...


通過前面幾篇文章的學習,大家應能掌握幾種容器類型的常見用法,對於簡單的增刪改和遍歷操作,各容器實例都提供了相應的處理方法,對於實際開發中頻繁使用的清單List,還能利用Arrays工具的asList方法給清單對象做初始化賦值,另外提供了專門的Collections工具進行排序、求最大元素、求最小元素等操作。那麼涉及到更加複雜的數據處理,游蕩如何有針對性地篩選和進一步加功能?
依次遍歷目標容器,對所有元素逐個加以分析判斷,並酌情將具體數據調整至滿意的狀態,這種千篇一律的業務流程固然能夠解決問題,可惜由此帶來的副作用是顯而易見的,包括但不限於:代碼冗長、分支眾多、邏輯繁瑣、不易重用等等。為了改進相關業務邏輯的編程方式,幫助開發者形成良好的編碼風格,Java的每次版本更新都試圖給出有效的解決方案,其中影響深遠的當數Java8推出的兩項新特性:新增的泛型介面與流式處理。關於前一個泛型介面特性,用於容器操作的泛型介面主要有三個,分別是斷言介面、消費介面和函數介面,有關的應用案例可參見之前的泛型介面文章,這裡不再贅述。真正具有革命性意義的才是本文的主角——流式處理。
所謂流,隱含著流水線的意思,也就是由開發者事先設定一批處理指令,說明清楚每條指令的前因後果,然後啟動流水線作業,即可得到最終的處理結果。流式處理的精髓在於一氣呵成,只要萬事俱備,決不拖泥帶水。開展流式處理主要包括三個步驟:獲得容器的流對象、設置流的各項篩選和加工指令,以及規劃處理結果的展示形式。下麵就分別予以詳細介紹。

1、獲得容器的流對象
Java8給每種容器都準備了兩條流水線,一條是串列流,另一條是並行流。串列流顧名思義各項任務是前後串在一起的,只有處理完前一項任務,才能繼續執行後一項任務。調用容器實例的stream方法即可獲得該容器的串列流對象,而調用容器實例的parallelStream方法可獲得該容器的並行流對象。
流對象的獲取操作同時也是流式處理的開始指令,每次進行流式處理之前,都必須先獲取當前容器的流對象,要麼獲取串列流,要麼獲取並行流。

2、設置流的各項篩選和加工指令
不管是串列流還是並行流,它們承載的都是容器內部的原始數據,這些原材料要經過各道加工工序,之後才會得到具備初步形態的半成品。加工數據期間所調用的流方法說明如下:
filter:按照指定條件過濾。即篩選出符合條件的那部分數據。
sorted:根據指定欄位對所有記錄排序。可選擇升序或者降序。
map:映射成指定的數據類型。
limit:只取前面若幹條數據。
distinct:去掉重覆記錄。保證每條記錄都是唯一的。
以上的加工方法屬於流式處理的中間指令,每次流水線作業都允許設置一條或者多條中間指令。

3、規劃處理結果的展示形式
前一步的各項加工處理完畢,還要弄個包裝才能輸出最終的成品,也就是這條流水線生產出來的數據到底長什麼模樣。結果數據的記錄包裝有三種形式,分別對應如下的三個方法:
count:統計結果數據的數量。
forEach:依次遍歷結果數據,並逐條進行個性化處理。
collect:搜集和整理結果數據,並返回指定格式的清單記錄。
上面的三個包裝方法屬於流式處理的結束指令,每次流水線作業必須配備有且僅有其中的一條結束指令。

接下來列舉幾個實際應用的業務場景,看看採取流式處理時該如何編碼。首先準備一個原始的蘋果清單,後續將對這個蘋果清單發動流水作業。原始清單的獲取代碼示例如下:

	// 獲取預設的蘋果清單
	private static ArrayList<Apple> getAppleList() {
		ArrayList<Apple> appleList = new ArrayList<Apple>();
			appleList.add(new Apple("紅蘋果", "RED", 150d, 10d));
			appleList.add(new Apple("大蘋果", "green", 250d, 10d));
			appleList.add(new Apple("紅蘋果", "red", 300d, 10d));
			appleList.add(new Apple("大蘋果", "yellow", 200d, 10d));
			appleList.add(new Apple("紅蘋果", "green", 100d, 10d));
			appleList.add(new Apple("大蘋果", "Red", 250d, 10d));
		return appleList;
	}

 

然後需要統計紅蘋果總數的話,可通過下列的流式代碼開展統計操作:

		// 統計紅蘋果的總數
		long redCount = getAppleList().stream() // 串列處理
				.filter(Apple::isRedApple) // 過濾條件。專門挑選紅蘋果
				.count(); // 統計記錄個數
		System.out.println("紅蘋果總數=" + redCount);

 

註意到上述代碼的filter方法內部出現了方法引用,的確流式處理的主要方法都預留了函數式介面的調用,所以經常會在流式代碼中看到五花八門的方法引用與Lambda表達式。比如下麵的結果遍歷代碼就在forEach方法中填充了Lambda表達式:

		// 對每個紅蘋果依次進行處理
		getAppleList().stream() // 串列處理
				.filter(Apple::isRedApple) // 過濾條件。專門挑選紅蘋果
				.forEach(s -> System.out.println("當前顏色為"+s.getColor())); // 逐條開展操作

 

當然流水作業更常見的輸出另一串清單數據,此時流式處理的結束指令就得採用collect方法。下麵便是從原始清單中挑出紅蘋果清單的流式代碼:

		// 挑出紅蘋果清單
		List<Apple> redAppleList = getAppleList().stream() // 串列處理
				//.parallelStream() // 並行處理
				.filter(Apple::isRedApple) // 過濾條件。專門挑選紅蘋果
				.sorted(Comparator.comparing(Apple::getWeight)) // 按蘋果重量升序排列
				//.sorted(Comparator.comparing(Apple::getWeight).reversed()) // 按蘋果重量降序排列
				.limit(3) // 只取前幾條數據
				.distinct() // 去掉重覆記錄
				.collect(Collectors.toList()); // 返回一串清單
		System.out.println("紅蘋果清單=" + redAppleList.toString());

 

結果清單可能不需要完整的蘋果信息,只需列出蘋果名稱欄位,那麼得調用map方法把完整的蘋果信息映射為單個的名稱欄位。此時的篩選代碼變成下麵這樣:

		// 挑出去重後的蘋果名稱清單
		List<String> allNameList = getAppleList().stream() // 串列處理
				.map(Apple::getName) // 映射成新的數據類型
				.distinct() // 去掉重覆記錄
				.collect(Collectors.toList()); // 返回一串清單
		System.out.println("蘋果名稱去重後的清單=" + allNameList.toString());

 

除了普通的清單,collect方法還能返回分組清單,也就是把結果數據按照某種條件進行分組,再統計每個分組的成員數目。仍以蘋果清單為例,紅蘋果可通過名稱或者產地分組,分組的同時計算每個小組裡各有多少粒蘋果。於是形成了以下的分組計數代碼:

		// 按照名稱統計紅蘋果的分組個數
		Map<String, Long> redStatisticCount = getAppleList().stream() // 串列處理
				.filter(Apple::isRedApple) // 過濾條件。專門挑選紅蘋果
				.collect(Collectors.groupingBy(Apple::getName, Collectors.counting())); // 返回分組計數
		System.out.println("紅蘋果分組計數=" + redStatisticCount.toString());

 

分組計數僅僅是簡單統計各組的成員數量,有時還想單獨計算某個欄位的統計值,比如每個小組裡的蘋果總價各是多少?這時collect方法必須同時完成兩項任務,第一項要根據某種條件分組,第二項要對各組的蘋果價格求和,如此改造之後的分組求和代碼如下所示:

		// 按照名稱統計紅蘋果的分組總價
		Map<String, Double> redPriceSum = getAppleList().stream() // 串列處理
				.filter(Apple::isRedApple) // 過濾條件。專門挑選紅蘋果
				.collect(Collectors.groupingBy(Apple::getName, Collectors.summingDouble(Apple::getPrice))); // 返回分組並對某欄位求和
		System.out.println("紅蘋果分組總價=" + redPriceSum.toString());

 

觀察以上的具體案例,發現流式處理的代碼相當連貫,每個步驟該做什麼事情都一清二楚,中間沒有許多繁複的流程式控制制,唯有一條條分工明確的處理指令,同時充分發揮了方法引用及Lambda表達式的便利性,使得原本令人頭痛的容器加工變成了有章可循的流水線作業,從而極大地提高了開發者的編碼效率。



更多Java技術文章參見《Java開發筆記(序)章節目錄


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 都說三十而立,但現在三十成了程式員的一道坎,如果近年來你有過求職的經歷,或是你關註過智聯、前程、獵聘等招聘平臺,你會發現平臺上數以萬計的招聘信息都會要求應聘者年齡在35歲以內,對於部分科技類初創企業,公司寧願放寬教育程度的要求。也就是說,如果你的年齡大於30歲,那就不要費神來申請了,因為你很可能在自 ...
  • 一、什麼是面向對象 在用面向對象思想寫代碼之前,先瞭解一下什麼是面向對象? 個人理解: 面向對象:把現實世界里的具體物體或者邏輯世界的邏輯物體,用抽象手段,把這些物體抽象成程式能夠識別的類,使類具備物體的屬性和行為,把物體與物體之間的關聯轉換成類與類之間的關聯,用編程邏輯把這些關聯表示出來設計成程式 ...
  • import java.util.Scanner;public class Main{public static void main(String[] args) {int maxn=1000000+5;int mod=10007;int[] Fibonacci=new int[maxn];Fibo ...
  • Python描述符的使用 前言 作為一位python的使用者,你可能使用python有一段時間了,但是對於python中的描述符卻未必使用過,接下來是對描述符使用的介紹 場景介紹 為了引入描述符的使用,我們先設計一個非常簡單的類: class Product(): def __init__(self ...
  • 加密視頻 在以後的開發項目中,很可能有做線上視頻的,而線上視頻就有個問題,因為線上播放,就很有可能視頻數據被抓包,如果這個線上視頻平臺有付費視頻的話,這樣就會有人做點倒賣視頻的生意了,針對這個問題,目前國內有很多不錯的加密視頻平臺,可以把你平臺的視頻放在他們那裡,然後通過他們的機制進行加密,然後做一... ...
  • 個人筆記,如有疏漏,還請指正。 使用多線程(threading)和多進程(multiprocessing)完成常規的併發需求,在啟動的時候 start、join 等步驟不能省,複雜的需要還要用 1 2 個隊列。 隨著需求越來越複雜,如果沒有良好的設計和抽象這部分的功能層次,代碼量越多調試的難度就越大 ...
  • 一、問題 BIO 和 NIO 作為 Server 端,當建立了 10 個連接時,分別產生多少個線程? 答案: 因為傳統的 IO 也就是 BIO 是同步線程堵塞的,所以每個連接都要分配一個專用線程來處理請求,這樣 10 個連接就會創建 10 個線程去處理。而 NIO 是一種同步非阻塞的 I/O 模型, ...
  • springboot 項目中讀取資源文件內容 如圖片、文檔文件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...