Java8 stream 中利用 groupingBy 進(jìn)行多字段分組求和案例
Java8的groupingBy實(shí)現(xiàn)集合的分組,類似Mysql的group by分組功能,注意得到的是一個(gè)map
對集合按照單個(gè)屬性分組、分組計(jì)數(shù)、排序
List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya"); // 分組 Map<String, List<String>> result1 = items.stream().collect( Collectors.groupingBy( Function.identity() ) ); //{papaya=[papaya], orange=[orange], banana=[banana, banana], apple=[apple, apple, apple]} System.out.println(result1); // 分組計(jì)數(shù) Map<String, Long> result2 = items.stream().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) ); // {papaya=1, orange=1, banana=2, apple=3} System.out.println(result2); Map<String, Long> finalMap = new LinkedHashMap<>(); //分組, 計(jì)數(shù)和排序 result2.entrySet().stream() .sorted(Map.Entry.<String, Long>comparingByValue().reversed()) .forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue())); // {apple=3, banana=2, papaya=1, orange=1} System.out.println(finalMap);
集合按照多個(gè)屬性分組
1.多個(gè)屬性拼接出一個(gè)組合屬性
public static void main(String[] args) { User user1 = new User("zhangsan", "beijing", 10); User user2 = new User("zhangsan", "beijing", 20); User user3 = new User("lisi", "shanghai", 30); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); Map<String, List<User>> collect = list.stream().collect(Collectors.groupingBy(e -> fetchGroupKey(e))); //{zhangsan#beijing=[User{age=10, name='zhangsan', address='beijing'}, User{age=20, name='zhangsan', address='beijing'}], // lisi#shanghai=[User{age=30, name='lisi', address='shanghai'}]} System.out.println(collect); } private static String fetchGroupKey(User user){ return user.getName() +"#"+ user.getAddress(); }
2.嵌套調(diào)用groupBy
User user1 = new User("zhangsan", "beijing", 10); User user2 = new User("zhangsan", "beijing", 20); User user3 = new User("lisi", "shanghai", 30); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); Map<String, Map<String, List<User>>> collect = list.stream().collect( Collectors.groupingBy( User::getAddress, Collectors.groupingBy(User::getName) ) ); System.out.println(collect);
3. 使用Arrays.asList
我有一個(gè)與Web訪問記錄相關(guān)的域?qū)ο罅斜怼_@些域?qū)ο罂梢詳U(kuò)展到數(shù)千個(gè)。
我沒有資源或需求將它們以原始格式存儲在數(shù)據(jù)庫中,因此我希望預(yù)先計(jì)算聚合并將聚合的數(shù)據(jù)放在數(shù)據(jù)庫中。
我需要聚合在5分鐘窗口中傳輸?shù)目傋止?jié)數(shù),如下面的sql查詢
select round(request_timestamp, '5') as window, --round timestamp to the nearest 5 minute cdn, isp, http_result_code, transaction_time, sum(bytes_transferred) from web_records group by round(request_timestamp, '5'), cdn, isp, http_result_code, transaction_time
在java 8中,我當(dāng)前的第一次嘗試是這樣的,我知道這個(gè)解決方案類似于Group by multiple field names in java 8
Map<Date, Map<String, Map<String, Map<String, Map<String, Integer>>>>>>> aggregatedData = webRecords .stream() .collect(Collectors.groupingBy(WebRecord::getFiveMinuteWindow, Collectors.groupingBy(WebRecord::getCdn, Collectors.groupingBy(WebRecord::getIsp, Collectors.groupingBy(WebRecord::getResultCode, Collectors.groupingBy(WebRecord::getTxnTime, Collectors.reducing(0, WebRecord::getReqBytes(), Integer::sum)))))));
這是可行的,但它是丑陋的,所有這些嵌套的地圖是一個(gè)噩夢!要將地圖“展平”或“展開”成行,我必須這樣做
for (Date window : aggregatedData.keySet()) { for (String cdn : aggregatedData.get(window).keySet()) { for (String isp : aggregatedData.get(window).get(cdn).keySet()) { for (String resultCode : aggregatedData.get(window).get(cdn).get(isp).keySet()) { for (String txnTime : aggregatedData.get(window).get(cdn).get(isp).get(resultCode).keySet()) { Integer bytesTransferred = aggregatedData.get(window).get(cdn).get(distId).get(isp).get(resultCode).get(txnTime); AggregatedRow row = new AggregatedRow(window, cdn, distId...
如你所見,這是相當(dāng)混亂和難以維持。
有誰知道更好的方法嗎?任何幫助都將不勝感激。
我想知道是否有更好的方法來展開嵌套的映射,或者是否有一個(gè)庫允許您對集合進(jìn)行分組。
最佳答案
您應(yīng)該為地圖創(chuàng)建自定義密鑰。最簡單的方法是使用Arrays.asList:
Function<WebRecord, List<Object>> keyExtractor = wr -> Arrays.<Object>asList(wr.getFiveMinuteWindow(), wr.getCdn(), wr.getIsp(), wr.getResultCode(), wr.getTxnTime()); Map<List<Object>, Integer> aggregatedData = webRecords.stream().collect( Collectors.groupingBy(keyExtractor, Collectors.summingInt(WebRecord::getReqBytes)));
在這種情況下,鍵是按固定順序列出的5個(gè)元素。不是很面向?qū)ο?,但很簡單?;蛘撸梢远x自己的表示自定義鍵的類型,并創(chuàng)建適當(dāng)?shù)膆ashCode/equals實(shí)現(xiàn)。
補(bǔ)充知識:java8 新特性 Stream流 分組 排序 過濾 多條件去重 (最小、最大、平均、求和)
什么是 Stream?
Stream 是用函數(shù)式編程方式在集合類上進(jìn)行復(fù)雜操作的工具,其集成了Java 8中的眾多新特性之一的聚合操作,開發(fā)者可以更容易地使用Lambda表達(dá)式,并且更方便地實(shí)現(xiàn)對集合的查找、遍歷、過濾以及常見計(jì)算等。話不多說,直接上代碼。
List<User> list = new ArrayList<User>(); list = Arrays.asList( new User("小強(qiáng)", 11, "男"), new User("小玲", 15, "女"), new User("小虎", 23, "男"), new User("小雨", 26, "女"), new User("小飛", 19, "男"), new User("小玲", 15, "女") ); //分組 Map<String, List<User>> listMap = list.stream().collect(Collectors.groupingBy(User::getSex)); for(String key:listMap.keySet()){ System.out.print(key+"組:"); listMap.get(key).forEach(user -> System.out.print(user.getName())); System.out.println(); } //排序 list.stream().sorted(Comparator.comparing(user-> user.getAge())) .forEach(user -> System.out.println(user.getName())); //過濾 list.stream().filter(user -> user.getSex().equals("男")).collect(Collectors.toList()) .forEach(user -> System.out.println(user.getName())); //多條件去重 list.stream().collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet<>( Comparator.comparing(user -> user.getAge() + ";" + user.getName()))), ArrayList::new)) .forEach(user -> System.out.println(user.getName())); //最小值 Integer min = list.stream().mapToInt(User::getAge).min().getAsInt(); //最大值 Integer max = list.stream().mapToInt(User::getAge).max().getAsInt(); //平均值 Double average = list.stream().mapToInt(User::getAge).average().getAsDouble(); //和 Integer sum = list.stream().mapToInt(User::getAge).sum(); System.out.println("最小值:"+min+", 最大值"+max+", 平均值:"+average+", 和:"+sum); //分組求和 Map<String, IntSummaryStatistics> collect = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.summarizingInt(User::getAge))); IntSummaryStatistics statistics1 = collect.get("男"); IntSummaryStatistics statistics2 = collect.get("女"); System.out.println(statistics1.getSum()); System.out.println(statistics1.getAverage()); System.out.println(statistics1.getMax()); System.out.println(statistics1.getMin()); System.out.println(statistics1.getCount()); System.out.println(statistics2.getSum()); System.out.println(statistics2.getAverage()); System.out.println(statistics2.getMax()); System.out.println(statistics2.getMin()); System.out.println(statistics2.getCount()); //提取list中兩個(gè)屬性值,轉(zhuǎn)為map Map<String, String> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex)); System.out.println(JsonUtil.toJson(userMap)) //取出所有名字 List<String> names = list.stream().map(User::getName).collect(Collectors.toList()); System.out.println(JsonUtil.toJson(names))
以上這篇Java8 stream 中利用 groupingBy 進(jìn)行多字段分組求和案例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java設(shè)計(jì)模式七大原則之里氏替換原則詳解
在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,里氏替換原則(Liskov Substitution principle)是對子類型的特別定義。本文將為大家詳細(xì)介紹Java設(shè)計(jì)模式七大原則之一的里氏替換原則,需要的可以參考一下2022-02-02Red?Hat?安裝JDK與IntelliJ?IDEA的詳細(xì)過程
YUM是基于Red Hat的Linux發(fā)行版的一個(gè)強(qiáng)大而用戶友好的包管理工具,這篇文章主要介紹了Red?Hat安裝JDK與IntelliJ IDEA,需要的朋友可以參考下2023-08-08Java,JSP,Servlet獲取當(dāng)前工程路徑(絕對路徑)問題解析
這篇文章主要介紹了Java,JSP,Servlet獲取當(dāng)前工程路徑(絕對路徑)問題解析,需要的朋友可以參考下。2017-09-09非常適合新手學(xué)生的Java線程池超詳細(xì)分析
作者是一個(gè)來自河源的大三在校生,以下筆記都是作者自學(xué)之路的一些淺薄經(jīng)驗(yàn),如有錯(cuò)誤請指正,將來會(huì)不斷的完善筆記,幫助更多的Java愛好者入門2022-03-03Java關(guān)鍵字instanceof用法及實(shí)現(xiàn)策略
instanceof 運(yùn)算符是用來在運(yùn)行時(shí)判斷對象是否是指定類及其父類的一個(gè)實(shí)例。這篇文章主要介紹了Java關(guān)鍵字instanceof用法解析,需要的朋友可以參考下2020-08-08SpringMVC通過攔截器實(shí)現(xiàn)IP黑名單
這篇文章主要為大家詳細(xì)介紹了SpringMVC通過攔截器實(shí)現(xiàn)IP黑名單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Java?LocalDateTime獲取時(shí)間信息、格式化、轉(zhuǎn)換為數(shù)字時(shí)間戳代碼示例
其實(shí)我們在Java項(xiàng)目中對日期進(jìn)行格式化,主要是利用一些日期格式化類,下面這篇文章主要給大家介紹了關(guān)于Java?LocalDateTime獲取時(shí)間信息、格式化、轉(zhuǎn)換為數(shù)字時(shí)間戳的相關(guān)資料,需要的朋友可以參考下2023-11-11利用Spring JPA中的@Version注解實(shí)現(xiàn)樂觀鎖
樂觀鎖是數(shù)據(jù)庫和應(yīng)用程序中使用的一種并發(fā)控制策略,用于在多個(gè)事務(wù)嘗試更新單個(gè)記錄時(shí)確保數(shù)據(jù)完整性,Java Persistence API (JPA) 提供了一種借助@Version注解在 Java 應(yīng)用程序中實(shí)現(xiàn)樂觀鎖的機(jī)制,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下2023-11-11