1. 程式人生 > 程式設計 >Java8 stream 中利用 groupingBy 進行多欄位分組求和案例

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 進行多欄位分組求和案例就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。