前面一篇 Java 8 的 groupingBy 能否产生空的 Map 分组 是提出来的思考,本篇就是上一篇的答案。
由于在 Java 8 中用 Collectors.groupingBy 对 List 进行分组时每个组里都必须存在元素,也就是
Stream<Person> stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male"));
System.out.println(stream.collect(Collectors.groupingBy(person -> person.gender)));
只能得到结果
{male=[Tom, Jerry]}
而无法表示存在其他 gender 的可能性,并且 female=[] 的情况,即想要结果
{male=[Tom, Jerry], female=[]}
如果想得到以上的结果该当如何呢? stream.collect() 接受一个 Collector, Collectors 中只是定义了许多常用的 Collector 实现,如果不够用的话我们可以实现自己的 Collector. 下面就来定义一个 GroupingWithKeys, 它需要实现 java.util.stream.Collector 接口,有五个接口方法. 事成之后我们写
1 2 |
Stream<Person> stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male")); System.out.println(stream.collect(new GroupingWithKeys<>(person -> person.gender, "male", "female"))); |
能够得到输出
{male=[Tom, Jerry], female=[]}
下面是 Person 和 GroupingWithKeys 两个类的完整代码
GroupingWithKeys.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
class GroupingWithKeys<T, K> implements Collector<T, Map<K, List<T>>, Map<K, List<T>>> { private List<K> possibleKeys = Collections.emptyList(); private Function<T, K> keyGenerator; public GroupingWithKeys(Function<T, K> keyGenerator, K...possibleKeys) { //构造时传入 Key 生成器和可能的 Keys if(possibleKeys != null) { this.possibleKeys = Arrays.asList(possibleKeys); } this.keyGenerator = keyGenerator; } @Override public Supplier<Map<K, List<T>>> supplier() { return () -> { Map<K, List<T>> map = new LinkedHashMap<>(); possibleKeys.forEach(s -> map.put(s, new ArrayList<T>())); //按 possibleKeys 依次用空列表填充 Map return map; }; } @Override public BiConsumer<Map<K, List<T>>, T> accumulator() { return (map, t) -> { List<T> list = map.getOrDefault(keyGenerator.apply(t), new ArrayList<T>()); list.add(t); map.put(keyGenerator.apply(t), list); }; } @Override public BinaryOperator<Map<K, List<T>>> combiner() { return (map1, map2) -> { map1.putAll(map2); return map1; }; } @Override public Function<Map<K, List<T>>, Map<K, List<T>>> finisher() { return Function.identity(); } @Override public Set<Characteristics> characteristics() { return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT)); } } |
在上面的 supplier() 方法中为所有可能的 Keys 准备好一个空的 List, 然后填充好 Map .
Person.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Person { public final String name; public final String gender; public Person(String name, String gender) { this.name = name; this.gender = gender; } @Override public String toString() { return name; } } |
Java 既然提供了 java.util.stream.Collector 接口让我们扩展,那么想要什么样的 Collector 就自己创建吧。
本文链接 https://yanbin.blog/java-8-groupingby-empty-map-group/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。