2017-03-23 97 views
5

我使用groupingBymappingreducing 以下問題的解決方案發揮各地:Elegantly create map with object fields as key/value from object stream in Java 8。總結的目的是爲了獲得與年齡鍵和一個人作爲Set的愛好的地圖。的Java 8 stream.collect(... groupingBy(...映射(...還原)))減少BinaryOperator使用率

一個我想出瞭解決方案(不是很好,但是這不是重點),有一個奇怪的行爲。

用下面的列表作爲輸入:

List<Person> personList = Arrays.asList(
    new Person(/* name */ "A", /* age */ 23, /* hobbies */ asList("a")), 
    new Person("BC", 24, asList("b", "c")), 
    new Person("D", 23, asList("d")), 
    new Person("E", 23, asList("e")) 
); 

及以下解決方案:

Collector<List<String>, ?, Set<String>> listToSetReducer = Collectors.reducing(new HashSet<>(), HashSet::new, (strings, strings2) -> { 
    strings.addAll(strings2); 
    return strings; 
}); 
Map<Integer, Set<String>> map = personList.stream() 
              .collect(Collectors.groupingBy(o -> o.age, 
                     Collectors.mapping(o -> o.hobbies, listToSetReducer))); 
System.out.println("map = " + map); 

我:

map = {23=[a, b, c, d, e], 24=[a, b, c, d, e]} 

顯然不是我所期待的。我寧願預計:

map = {23=[a, d, e], 24=[b, c]} 

現在,如果我只是取代的二元運算(strings, strings2)的順序(降低集電極),以(strings2, strings)我得到預期的結果。那麼我在這裏錯過了什麼? 我有沒有曲解reducing -collector?或者我錯過了哪些文檔,很明顯我的使用不能按預期工作?

Java版本是1.8.0_121如果該事項。

回答

9

減少不應該修改傳入的對象。在你的情況下,你正在修改應該是身份值的傳入HashSet並將其返回,所以所有組都將具有與結果相同的HashSet實例,其中包含所有值。

你需要的是一個Mutable Reduction,可以通過Collector.of(…)實現像它已與預置的收藏家Collectors.toList()Collectors.toSet()了已經實施等

Map<Integer, Set<String>> map = personList.stream() 
    .collect(Collectors.groupingBy(o -> o.age, 
     Collector.of(HashSet::new, (s,p) -> s.addAll(p.hobbies), (s1,s2) -> { 
      s1.addAll(s2); 
      return s1; 
     }))); 

的原因,我們需要一個自定義的收藏家都,是Java 8不具有flatMapping收集器,它的Java 9將要介紹。就這樣,該解決方案將是這樣的:

Map<Integer, Set<String>> map = personList.stream() 
    .collect(Collectors.groupingBy(o -> o.age, 
     Collectors.flatMapping(p -> p.hobbies.stream(), Collectors.toSet()))); 
+0

有趣..起初,我甚至試圖找到'flatMapping'在'Collectors'(或申請'hobbies.stream()',並與它的工作) 。很高興知道它即將到來。哦,親愛的,我把混合器與操作員混在一起。所以,只有切換參數纔是正確的結果。感謝您的澄清! – Roland

+2

是的,在順序上下文中,縮減函數總是被評估爲'f(previous,next)',而'previous'將是第一次評估的身份值和後續評估的先前結果。因此'(a,b) - > a'將始終以標識值結束,而'(a,b) - > b'將使用映射器函數創建的新集。但在並行評估中,兩個參數都可能是之前部分評估的結果,而且由於部分結果可能爲空,因此任一參數都可能是標識值,因此使用第二個參數不是可靠的修補程序。 – Holger