詳解Java Stream實現(xiàn)精準(zhǔn)數(shù)據(jù)分組與比例計算技巧
前言
在當(dāng)今數(shù)據(jù)驅(qū)動的軟件開發(fā)時代,數(shù)據(jù)的快速處理與分析能力是提升應(yīng)用性能和用戶體驗的關(guān)鍵。Java 8 引入的 Stream API 為開發(fā)者提供了一種強大且優(yōu)雅的方式來處理集合數(shù)據(jù),它不僅簡化了代碼,還通過內(nèi)置的并行處理能力大幅提升了性能。在眾多應(yīng)用場景中,分類占比統(tǒng)計是數(shù)據(jù)處理中一個常見的需求。無論是對用戶行為數(shù)據(jù)進行分析,還是對業(yè)務(wù)數(shù)據(jù)進行分類匯總,快速準(zhǔn)確地統(tǒng)計各類數(shù)據(jù)的占比都能為決策提供有力支持。本文將深入探討如何利用 Java 8 Stream API 實現(xiàn)高效、簡潔且易于維護的分類占比統(tǒng)計,幫助開發(fā)者在實戰(zhàn)中快速掌握這一實用技巧。

在實際開發(fā)中,我們經(jīng)常遇到需要對集合中的數(shù)據(jù)按照某種類型進行分類統(tǒng)計的場景。例如,在電商系統(tǒng)中,可能需要統(tǒng)計不同品類商品的銷售占比;在用戶管理系統(tǒng)中,可能需要統(tǒng)計不同地區(qū)用戶的注冊占比。傳統(tǒng)的實現(xiàn)方式往往是通過循環(huán)遍歷集合,使用多個變量來分別統(tǒng)計每種類型的數(shù)量,然后再計算占比。這種做法不僅代碼冗長,而且在處理大規(guī)模數(shù)據(jù)時效率低下,容易出錯。Java 8 Stream API 提供了一種全新的解決方案,它允許我們以聲明式的方式處理數(shù)據(jù),通過一系列的中間操作和終端操作,可以輕松地實現(xiàn)分類統(tǒng)計。Stream 的 `groupingBy` 方法可以方便地對數(shù)據(jù)進行分組,而 `collectors` 提供的各種收集器則可以方便地對分組后的數(shù)據(jù)進行進一步的統(tǒng)計和處理。通過合理使用這些特性,我們可以寫出既簡潔又高效的代碼,快速完成分類占比統(tǒng)計的任務(wù)。
為了更好地理解如何使用 Java 8 Stream 實現(xiàn)分類占比統(tǒng)計,本文將通過具體的實戰(zhàn)案例進行詳細(xì)講解。首先,我們會介紹如何使用 Stream 對數(shù)據(jù)進行分組,然后通過收集器對分組后的數(shù)據(jù)進行統(tǒng)計,最后計算出每種類型的占比。在實戰(zhàn)案例中,我們將使用一個模擬的電商銷售數(shù)據(jù)集,展示如何統(tǒng)計不同品類商品的銷售占比。通過這個案例,讀者將能夠清晰地看到 Stream API 在處理分類占比統(tǒng)計問題時的優(yōu)勢,以及如何在實際項目中應(yīng)用這些技巧。此外,我們還會探討一些優(yōu)化技巧,如如何利用并行 Stream 進一步提升性能,以及如何處理可能出現(xiàn)的異常情況,確保代碼的健壯性。通過本文的介紹,相信讀者能夠掌握 Java 8 Stream 在分類占比統(tǒng)計方面的實戰(zhàn)應(yīng)用,將其應(yīng)用到自己的項目中,提高數(shù)據(jù)處理的效率和質(zhì)量。
一、需求描述
本節(jié)將重點對需求場景和排序需求進行介紹。
1、場景描述
在城市規(guī)劃與資源管理中,我們面臨著一個重要的數(shù)據(jù)分析需求:對不同機構(gòu)設(shè)施類型的分布情況進行分類統(tǒng)計與分析。具體而言,系統(tǒng)中存儲了大量關(guān)于各類機構(gòu)設(shè)施的記錄,包括設(shè)施類型、地理位置、服務(wù)范圍等關(guān)鍵信息。管理部門希望能夠快速了解各機構(gòu)設(shè)施類型在城市中的占比情況,以便優(yōu)化資源分配和城市規(guī)劃布局。例如,通過統(tǒng)計可以發(fā)現(xiàn)哪些設(shè)施類型在城市中分布廣泛,哪些設(shè)施類型相對稀缺,從而為政策制定和資源投入提供數(shù)據(jù)支持。隨著城市的發(fā)展,數(shù)據(jù)量不斷增加,傳統(tǒng)的統(tǒng)計方式已難以滿足快速響應(yīng)的需求,因此需要一種高效、簡潔且易于擴展的解決方案來應(yīng)對這一挑戰(zhàn)。
2、排序要求
在完成機構(gòu)設(shè)施類型的分類占比統(tǒng)計后,為了更好地呈現(xiàn)數(shù)據(jù)并支持決策,還需要對統(tǒng)計結(jié)果進行排序。具體來說,管理部門希望按照機構(gòu)設(shè)施類型占比的高低進行排序,以便快速識別出在城市中分布最廣泛和最稀缺的設(shè)施類型。此外,還可能需要根據(jù)機構(gòu)設(shè)施類型名稱進行字母順序排序,以方便在報告中進行展示。在排序過程中,需要確保排序的準(zhǔn)確性和穩(wěn)定性,避免因排序錯誤導(dǎo)致的決策失誤。同時,考慮到數(shù)據(jù)量可能較大,排序算法的效率也至關(guān)重要。因此,我們需要在實現(xiàn)分類占比統(tǒng)計的基礎(chǔ)上,進一步優(yōu)化代碼,使其能夠支持高效的排序操作,確保整個數(shù)據(jù)處理流程的流暢性和高效性。
二、Java1.8實現(xiàn)
本節(jié)將詳細(xì)介紹在Java1.8中如何使用Stream來進行機構(gòu)設(shè)施的分類占比統(tǒng)計以及排序。通過本節(jié)大家能快速掌握如何使用Java來實現(xiàn)這個需求。
1、分類對象實現(xiàn)
為了模擬對分類對象的描述和占比統(tǒng)計,我們需要定義一個分類占比統(tǒng)計對象,核心代碼如下,這個類中type用于對分類進行描述,而percentage則用于存儲占比比例:
package org.yelang.pcwater.riskevaluation;
/**
* 自定義類用于存儲類型和占比
*/
public class TypePercentage {
private String type;
private double percentage;
public TypePercentage(String type, double percentage) {
this.type = type;
this.percentage = percentage;
}
public String getType() {
return type;
}
public double getPercentage() {
return percentage;
}
@Override
public String toString() {
return String.format("TypePercentage{type='%s', percentage=%.1f%%}", type, percentage * 100);
}
}2、Stream進行分組排序
通常在進行數(shù)據(jù)統(tǒng)計時,我們首先需要生成不同的機構(gòu)設(shè)施類型,然后將這些數(shù)據(jù)添加到集合中,然后將集合作為入?yún)魅敕椒ㄖ羞M行處理。因此我們定義一個方法首先來實現(xiàn)Stream的方式進行分布,核心代碼如下:
/**
* 獲取排序后的類型占比列表
*
* @param dataList 數(shù)據(jù)列表
* @return 按占比降序排序的類型占比列表
*/
public static List<TypePercentage> getSortedTypePercentages(List<Map<String, Object>> dataList) {
if (dataList == null || dataList.isEmpty()) {
return new ArrayList<>();
}
// 統(tǒng)計每個 type 出現(xiàn)的次數(shù)
Map<String, Long> typeCount = dataList.stream()
.filter(map -> map.containsKey("type") && map.get("type") != null)
.collect(Collectors.groupingBy(map -> {
// 將 Object 類型的 type 轉(zhuǎn)換為 String
Object typeObj = map.get("type");
if (typeObj instanceof String) {
return (String) typeObj;
} else {
// 如果不是 String 類型,使用 toString() 方法轉(zhuǎn)換
return typeObj.toString();
}
}, Collectors.counting()));
// 計算總數(shù)量
long total = typeCount.values().stream().mapToLong(Long::longValue).sum();
// 計算占比并創(chuàng)建對象,按占比降序排序
return typeCount.entrySet().stream().map(entry -> {
double percentage = (double) entry.getValue() / total;
return new TypePercentage(entry.getKey(), percentage);
}).sorted((tp1, tp2) -> Double.compare(tp2.getPercentage(), tp1.getPercentage())) // 降序排序
.collect(Collectors.toList());
}3、實例調(diào)用
在準(zhǔn)備調(diào)用方法之前,首先我們先來準(zhǔn)備這些模擬的機構(gòu)信息,模擬代碼如下:
// 原始數(shù)據(jù) - JDK 1.8 創(chuàng)建方式
List<Map<String, Object>> dataList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "市人民醫(yī)院");
map1.put("type", "醫(yī)院");
dataList.add(map1);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "幸福小學(xué)");
map2.put("type", "學(xué)校");
dataList.add(map2);
Map<String, Object> map3 = new HashMap<>();
map3.put("name", "市中心醫(yī)院");
map3.put("type", "醫(yī)院");
dataList.add(map3);
Map<String, Object> map4 = new HashMap<>();
map4.put("name", "第二實驗小學(xué)");
map4.put("type", "學(xué)校");
dataList.add(map4);
Map<String, Object> map5 = new HashMap<>();
map5.put("name", "市圖書館");
map5.put("type", "圖書館");
dataList.add(map5);
Map<String, Object> map6 = new HashMap<>();
map6.put("name", "建設(shè)銀行");
map6.put("type", "銀行");
dataList.add(map6);然后調(diào)用前面的Stream分組排序方法,最后對返回的結(jié)果進行輸出展示,調(diào)用代碼如下:
// 獲取排序后的類型占比列表
List<TypePercentage> result = getSortedTypePercentages(dataList);
// 輸出結(jié)果
System.out.println("Type 占比統(tǒng)計結(jié)果(按占比降序排序):");
result.forEach(typePercentage -> {
System.out.printf("%s: %.2f%%\n", typePercentage.getType(), typePercentage.getPercentage() * 100);
});最后來看一下最終的輸出結(jié)果:
Type 占比統(tǒng)計結(jié)果(按占比降序排序): 學(xué)校: 33.33% 醫(yī)院: 33.33% 銀行: 16.67% 圖書館: 16.67%
通過以上的代碼就可以實現(xiàn)按照分類來進行占比統(tǒng)計及排序,在一些統(tǒng)計業(yè)務(wù)中就可以進行靈活的統(tǒng)計實現(xiàn)。完整代碼如下:
package org.yelang.pcwater.riskevaluation;
import java.util.*;
import java.util.stream.Collectors;
/**
* 影響人群分布統(tǒng)計 *
*/
public class TypeStatisticSorted {
/**
* 獲取排序后的類型占比列表
*
* @param dataList 數(shù)據(jù)列表
* @return 按占比降序排序的類型占比列表
*/
public static List<TypePercentage> getSortedTypePercentages(List<Map<String, Object>> dataList) {
if (dataList == null || dataList.isEmpty()) {
return new ArrayList<>();
}
// 統(tǒng)計每個 type 出現(xiàn)的次數(shù)
Map<String, Long> typeCount = dataList.stream()
.filter(map -> map.containsKey("type") && map.get("type") != null)
.collect(Collectors.groupingBy(map -> {
// 將 Object 類型的 type 轉(zhuǎn)換為 String
Object typeObj = map.get("type");
if (typeObj instanceof String) {
return (String) typeObj;
} else {
// 如果不是 String 類型,使用 toString() 方法轉(zhuǎn)換
return typeObj.toString();
}
}, Collectors.counting()));
// 計算總數(shù)量
long total = typeCount.values().stream().mapToLong(Long::longValue).sum();
// 計算占比并創(chuàng)建對象,按占比降序排序
return typeCount.entrySet().stream().map(entry -> {
double percentage = (double) entry.getValue() / total;
return new TypePercentage(entry.getKey(), percentage);
}).sorted((tp1, tp2) -> Double.compare(tp2.getPercentage(), tp1.getPercentage())) // 降序排序
.collect(Collectors.toList());
}
public static void main(String[] args) {
// 原始數(shù)據(jù) - JDK 1.8 創(chuàng)建方式
List<Map<String, Object>> dataList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "市人民醫(yī)院");
map1.put("type", "醫(yī)院");
dataList.add(map1);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "幸福小學(xué)");
map2.put("type", "學(xué)校");
dataList.add(map2);
Map<String, Object> map3 = new HashMap<>();
map3.put("name", "市中心醫(yī)院");
map3.put("type", "醫(yī)院");
dataList.add(map3);
Map<String, Object> map4 = new HashMap<>();
map4.put("name", "第二實驗小學(xué)");
map4.put("type", "學(xué)校");
dataList.add(map4);
Map<String, Object> map5 = new HashMap<>();
map5.put("name", "市圖書館");
map5.put("type", "圖書館");
dataList.add(map5);
Map<String, Object> map6 = new HashMap<>();
map6.put("name", "建設(shè)銀行");
map6.put("type", "銀行");
dataList.add(map6);
// 獲取排序后的類型占比列表
List<TypePercentage> result = getSortedTypePercentages(dataList);
// 輸出結(jié)果
System.out.println("Type 占比統(tǒng)計結(jié)果(按占比降序排序):");
result.forEach(typePercentage -> {
System.out.printf("%s: %.2f%%\n", typePercentage.getType(), typePercentage.getPercentage() * 100);
});
}
}三、總結(jié)
以上就是本文的主要內(nèi)容,本文將通過具體的實戰(zhàn)案例進行詳細(xì)講解。首先,我們會介紹如何使用 Stream 對數(shù)據(jù)進行分組,然后通過收集器對分組后的數(shù)據(jù)進行統(tǒng)計,最后計算出每種類型的占比。行文倉促,定有不足之處,歡迎各位朋友在評論區(qū)批評指正,不勝感激。
以上就是詳解Java Stream實現(xiàn)精準(zhǔn)數(shù)據(jù)分組與比例計算技巧的詳細(xì)內(nèi)容,更多關(guān)于Java Stream數(shù)據(jù)分組的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot集成Session的實現(xiàn)示例
Session是一個在Web開發(fā)中常用的概念,它表示服務(wù)器和客戶端之間的一種狀態(tài)管理機制,用于跟蹤用戶在網(wǎng)站或應(yīng)用程序中的狀態(tài)和數(shù)據(jù),本文主要介紹了SpringBoot集成Session的實現(xiàn)示例,感興趣的可以了解一下2023-09-09
如何基于Idea遠(yuǎn)程調(diào)試tomcat war包及jar包
這篇文章主要介紹了如何基于Idea遠(yuǎn)程調(diào)試tomcat war包及jar包,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
SpingMvc復(fù)雜參數(shù)傳收總結(jié)
這篇文章主要為大家介紹了SpingMvc復(fù)雜參數(shù)傳收總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
springmvc開啟異步請求報錯Java code using the Ser
這篇文章主要為大家介紹了springmvc開啟異步請求報錯Java code using the Servlet API or解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-02-02
mybatisPlus實現(xiàn)多表連接分頁動態(tài)查詢方式
文章主要講述了Java開發(fā)中常用的一些設(shè)計模式和代碼結(jié)構(gòu),包括實體類(VO)、Mapper接口、Service實現(xiàn)類(ServiceImpl)等,通過這些組件的合理使用,可以提高代碼的可維護性和可擴展性2025-12-12
使用Spark?SQL實現(xiàn)讀取不帶表頭的txt文件
這篇文章主要為大家詳細(xì)介紹了如何使用Spark?SQL實現(xiàn)讀取不帶表頭的txt文件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03

