2015-07-21 143 views
4

隨着類:Java8流groupingBy枚舉和計數

public class Person { 

    private String name; 
    private Color favouriteColor; 
} 

public enum Color {GREEN, YELLOW, BLUE, RED, ORANGE, PURPLE} 

使用Java8流API有一個List<Person>我可以trasform它在一個Map<Color, Long>具有每Color的計數,也可用於不屬於色列入清單。 例子:

List<Person> list = List.of(
    new Person("Karl", Color.RED), 
    new Person("Greg", Color.BLUE), 
    new Person("Andrew", Color.GREEN) 
); 

Trasforming這個名單在地圖與所有與他們的計數枚舉的顏色。

感謝

解決

使用自定義收集解決:

public static <T extends Enum<T>> Collector<T, ?, Map<T, Long>> counting(Class<T> type) { 
    return Collectors.toMap(
     Function.<T>identity(), 
     x -> 1l, 
     Long::sum, 
     () -> new HashMap(Stream.of(type.getEnumConstants()).collect(Collectors.toMap(Function.<T>identity(),t -> 0l))) 
    ); 
} 


list.stream() 
    .map(Person::getFavouriteColor) 
    .collect(counting(Color.class)) 

回答

7

可以使用groupingBy收集器創建地圖,但如果你想爲無鍵添加默認值,你必須確保返回的地圖是由地圖提供Supplier可變的。在另一方面,這增加了機會,創造一個EnumMap哪個更適合於這種使用情況:

EnumMap<Color, Long> map = list.stream().collect(Collectors.groupingBy(
    Person::getFavouriteColor,()->new EnumMap<>(Color.class), Collectors.counting())); 
EnumSet.allOf(Color.class).forEach(c->map.putIfAbsent(c, 0L)); 

可能,你認爲它是清潔與供應商函數中的默認值來填充圖:

EnumMap<Color, Long> map = list.stream().collect(Collectors.toMap(
    Person::getFavouriteColor, x->1L, Long::sum,()->{ 
     EnumMap<Color, Long> em = new EnumMap<>(Color.class); 
     EnumSet.allOf(Color.class).forEach(c->em.put(c, 0L)); 
     return em; 
    })); 

但當然,你也可以使用流來創建初始地圖:

EnumMap<Color, Long> map = list.stream().collect(Collectors.toMap(
    Person::getFavouriteColor, x->1L, Long::sum,() -> 
     EnumSet.allOf(Color.class).stream().collect(Collectors.toMap(
     x->x, x->0L, Long::sum,()->new EnumMap<>(Color.class))))); 

但對於完整性,你可以做同樣沒有STRE上午API,如果你想:

EnumMap<Color, Long> map = new EnumMap<>(Color.class); 
list.forEach(p->map.merge(p.getFavouriteColor(), 1L, Long::sum)); 
EnumSet.allOf(Color.class).forEach(c->map.putIfAbsent(c, 0L)); 
+0

謝謝,我找到了一種方法來推廣所有枚舉類型的解決方案。 – rascio

3

你可以嘗試使用:

Map<Color, Long> counted = list.stream() 
     .collect(Collectors.groupingBy(Person::getFavouriteColor(), 
             Collectors.counting())); 

當然,這意味着你有一個getter Person#favouriteColor成員。

於是,爲了不存在的Color s添加到地圖,您可以流過所有Color值,過濾那些誰不作爲地圖尚鑰匙,並把它們與0值:

Stream.of(Color.values()) 
     .filter(x -> !counted.containsKey(x)) 
     .forEach(x -> counted.put(x, 0L)); 
+2

你可以用'Arrays.asList(Color.values())的forEach(X - > counted.putIfAbsent(X,0L));' –

+2

這是最好不要依賴。由Maping返回的映射是可變的。 Javadoc說:「對返回的Map的類型,可變性,可串行性或線程安全性沒有保證。」作爲第二個參數,使用帶'HashMap :: new'或'() - >新的EnumMap <>(Color.class)''的'groupingBy'的3參數版本會更好。 – Misha

+0

取決於你使用的是什麼,而不是依賴可變性,你可以按照上面描述的方法收集計數,並在查找時使用'counting.getOrDefault(value,0L)'。 –