Java8 流分组枚举和计数
Java8 stream groupingBy enum and counting
与类:
public class Person {
private String name;
private Color favouriteColor;
}
public enum Color {GREEN, YELLOW, BLUE, RED, ORANGE, PURPLE}
有一个 List<Person>
使用 Java8 流 API 我可以将它转换成一个 Map<Color, Long>
有每个 Color
的计数吗?列表中未包含的颜色。
示例:
List<Person> list = List.of(
new Person("Karl", Color.RED),
new Person("Greg", Color.BLUE),
new Person("Andrew", Color.GREEN)
);
用 all 枚举的颜色及其计数在地图中转换此列表。
谢谢
已解决
使用自定义收集器解决:
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))
您可以试试:
Map<Color, Long> counted = list.stream()
.collect(Collectors.groupingBy(Person::getFavouriteColor(),
Collectors.counting()));
当然,这意味着您有一个 getter 用于 Person#favouriteColor
成员。
然后,为了将不存在的 Color
添加到地图中,您可以流式处理所有 Color
值,过滤那些尚未用作地图键的值,然后将它们的值设置为 0
:
Stream.of(Color.values())
.filter(x -> !counted.containsKey(x))
.forEach(x -> counted.put(x, 0L));
您可以使用 groupingBy
收集器来创建映射,但是如果您想为不存在的键添加默认值,则必须通过为地图。另一方面,这增加了创建更适合此用例的 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));
可能是,您认为在 supplier 函数中使用默认值填充地图更清晰:
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)))));
但为了完整起见,如果您愿意,您可以在没有流 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));
与类:
public class Person {
private String name;
private Color favouriteColor;
}
public enum Color {GREEN, YELLOW, BLUE, RED, ORANGE, PURPLE}
有一个 List<Person>
使用 Java8 流 API 我可以将它转换成一个 Map<Color, Long>
有每个 Color
的计数吗?列表中未包含的颜色。
示例:
List<Person> list = List.of(
new Person("Karl", Color.RED),
new Person("Greg", Color.BLUE),
new Person("Andrew", Color.GREEN)
);
用 all 枚举的颜色及其计数在地图中转换此列表。
谢谢
已解决
使用自定义收集器解决:
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))
您可以试试:
Map<Color, Long> counted = list.stream()
.collect(Collectors.groupingBy(Person::getFavouriteColor(),
Collectors.counting()));
当然,这意味着您有一个 getter 用于 Person#favouriteColor
成员。
然后,为了将不存在的 Color
添加到地图中,您可以流式处理所有 Color
值,过滤那些尚未用作地图键的值,然后将它们的值设置为 0
:
Stream.of(Color.values())
.filter(x -> !counted.containsKey(x))
.forEach(x -> counted.put(x, 0L));
您可以使用 groupingBy
收集器来创建映射,但是如果您想为不存在的键添加默认值,则必须通过为地图。另一方面,这增加了创建更适合此用例的 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));
可能是,您认为在 supplier 函数中使用默认值填充地图更清晰:
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)))));
但为了完整起见,如果您愿意,您可以在没有流 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));