Java8 stream 中利用 groupingBy 進行多欄位分組求和案例
Java8的groupingBy實現集合的分組,類似Mysql的group by分組功能,注意得到的是一個map
對集合按照單個屬性分組、分組計數、排序
List<String> items = Arrays.asList("apple","apple","banana","orange","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); // 分組計數 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<>(); //分組,計數和排序 result2.entrySet().stream() .sorted(Map.Entry.<String,Long>comparingByValue().reversed()) .forEachOrdered(e -> finalMap.put(e.getKey(),e.getValue())); // {apple=3,papaya=1,orange=1} System.out.println(finalMap);
集合按照多個屬性分組
1.多個屬性拼接出一個組合屬性
public static void main(String[] args) { User user1 = new User("zhangsan","beijing",10); User user2 = new User("zhangsan",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,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.巢狀呼叫groupBy
User user1 = new User("zhangsan",10); User user2 = new User("zhangsan",20); User user3 = new User("lisi",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
我有一個與Web訪問記錄相關的域物件列表。這些域物件可以擴充套件到數千個。
我沒有資源或需求將它們以原始格式儲存在資料庫中,因此我希望預先計算聚合並將聚合的資料放在資料庫中。
我需要聚合在5分鐘視窗中傳輸的總位元組數,如下面的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,transaction_time
在java 8中,我當前的第一次嘗試是這樣的,我知道這個解決方案類似於Group by multiple field names in java 8
Map<Date,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)))))));
這是可行的,但它是醜陋的,所有這些巢狀的地圖是一個噩夢!要將地圖“展平”或“展開”成行,我必須這樣做
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,distId...
如你所見,這是相當混亂和難以維持。
有誰知道更好的方法嗎?任何幫助都將不勝感激。
我想知道是否有更好的方法來展開巢狀的對映,或者是否有一個庫允許您對集合進行分組。
最佳答案
您應該為地圖建立自定義金鑰。最簡單的方法是使用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個元素。不是很面向物件,但很簡單。或者,您可以定義自己的表示自定義鍵的型別,並建立適當的hashCode/equals實現。
補充知識:java8 新特性 Stream流 分組 排序 過濾 多條件去重 (最小、最大、平均、求和)
什麼是 Stream?
Stream 是用函數語言程式設計方式在集合類上進行復雜操作的工具,其集成了Java 8中的眾多新特性之一的聚合操作,開發者可以更容易地使用Lambda表示式,並且更方便地實現對集合的查詢、遍歷、過濾以及常見計算等。話不多說,直接上程式碼。
List<User> list = new ArrayList<User>(); list = Arrays.asList( new User("小強",11,"男"),new User("小玲",15,"女"),new User("小虎",23,new User("小雨",26,new User("小飛",19,"女") ); //分組 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中兩個屬性值,轉為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 進行多欄位分組求和案例就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。