2017-06-22 50 views
0

是否可以對Stream中的元素進行分組,然後繼續流式傳輸而不必從返回的地圖的EntrySet創建新的流?是否可以在不關閉流的情況下對元素進行分組?

例如,我可以這樣做:

public static void main(String[] args) { 
    // map of access date to list of users 
    // Person is a POJO with first name, last name, etc. 
    Map<Date, List<Person>> dateMap = new HashMap<>(); 
    // ... 
    // output, sorted by access date, then person last name 
    dateMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> { 
     Date date = e.getKey(); 
     // group persons by last name and sort 
     // this part seems clunky 
     e.getValue().stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet())) 
       .entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e2 -> { 
      // pool agent id is the key 
      String lastName = e2.getKey(); 
      Set<Person> personSet = e2.getValue(); 
      float avgAge = calculateAverageAge(personSet); 
      int numPersons = personSet.size(); 
      // write out row with date, lastName, avgAge, numPersons 
     }); 
    }); 
} 

這工作得很好,但似乎有點麻煩,尤其是流進一個地圖,然後立即進入集映射的流。

有沒有辦法將流中的對象分組,但繼續流式傳輸?

+2

簡單的答案是*不*;但可能你可以準確解釋你想要達到的目標(輸入和輸出),我們可以幫助解決這個問題? – Eugene

+0

@Eugene謝謝。這個問題恰恰包含了我想達到的目標,或多或少。我有dateMap的工作,我需要輸出行到按日期,然後姓氏,按日期或姓氏排序的報告。 – lucasvw

+1

在你的問題中沒有任何明顯的。我可以推論的是,你想按日期訂購人員,然後按姓氏。這種排序是否足夠,還是你真的需要將它們分組? – VGR

回答

1

您可以使用Map.forEach,下游收集器,TreeMap和IntSummaryStatistics縮短代碼。

通過分組到TreeMap(而不是將其保留到groupingBy收集器),您可以自動排序名稱。您不需要立即獲取分組地圖,而是添加一個summarizingInt收集器,將具有相同名稱的人員列表變成他們年齡的IntSummaryStatistics

public static void main(String[] args) { 
    Map<Date, List<Person>> dateMap = new HashMap<>(); 
    dateMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> { 
     Date date = e.getKey(); 

     e.getValue().stream() 
        .collect(Collectors.groupingBy(Person::getLastName, 
                TreeMap::new, 
                Collectors.summarizingInt(Person::getAge))) 
        .forEach((name, stats) -> System.out.println(date +" "+ 
                   lastName +" "+ 
                   stats.getAverage() +" "+ 
                   stats.getCount())); 
    }); 
} 

如果你有超過初始地圖類型控制,您可以使用TreeMap中有作爲,並進一步縮短:

public static void main(String[] args) { 
    Map<Date, List<Person>> dateMap = new TreeMap<>(); 
    dateMap.forEach((date, persons -> { ... 
+4

我知道,使用'TreeMap'來誘使地圖已經在'groupingBy'集合之後排序,但是與第一個直覺相反,收集到默認(散列)地圖和事後排序在大多數情況下會更有效案例。 – Holger

+0

@霍爾這是我的想法。謝謝你,馬爾特! – lucasvw

+0

@Holger感謝您指出。我認爲那裏可能有所不同,但希望縮短代碼。盧卡斯,如果性能影響值得關注,您仍然可以使用下游收集器彙總人員,並用'entrySet.stream.sorted.foreach'替換'forEach'。它和你的代碼幾乎一樣,但至少平均值已經計算出來了。 Holger,你有一個想法如何解決物化分組地圖? –

相關問題