Java 8 的 groupingBy 产生空的 Map 分组

前面一篇 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 接口,有五个接口方法. 事成之后我们写
1Stream<Person> stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male"));
2System.out.println(stream.collect(new GroupingWithKeys<>(person -> person.gender, "male", "female")));

能够得到输出
{male=[Tom, Jerry], female=[]}
下面是 Person 和 GroupingWithKeys 两个类的完整代码

GroupingWithKeys.java
 1class GroupingWithKeys<T, K> implements Collector<T, Map<K, List<T>>, Map<K, List<T>>> {
 2
 3  private List<K> possibleKeys = Collections.emptyList();
 4  private Function<T, K> keyGenerator;
 5
 6  public GroupingWithKeys(Function<T, K> keyGenerator, K...possibleKeys) {  //构造时传入 Key 生成器和可能的 Keys
 7    if(possibleKeys != null) {
 8      this.possibleKeys = Arrays.asList(possibleKeys);
 9    }
10    this.keyGenerator = keyGenerator;
11  }
12
13  @Override
14  public Supplier<Map<K, List<T>>> supplier() {
15    return () -> {
16      Map<K, List<T>> map = new LinkedHashMap<>();
17      possibleKeys.forEach(s -> map.put(s, new ArrayList<T>())); //按 possibleKeys 依次用空列表填充 Map
18      return map;
19    };
20  }
21
22  @Override
23  public BiConsumer<Map<K, List<T>>, T> accumulator() {
24    return (map, t) -> {
25      List<T> list = map.getOrDefault(keyGenerator.apply(t), new ArrayList<T>());
26      list.add(t);
27      map.put(keyGenerator.apply(t), list);
28    };
29  }
30
31  @Override
32  public BinaryOperator<Map<K, List<T>>> combiner() {
33    return (map1, map2) -> {
34      map1.putAll(map2);
35      return map1;
36    };
37  }
38
39  @Override
40  public Function<Map<K, List<T>>, Map<K, List<T>>> finisher() {
41    return Function.identity();
42  }
43
44  @Override
45  public Set<Characteristics> characteristics() {
46    return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
47  }
48}

在上面的 supplier() 方法中为所有可能的 Keys 准备好一个空的 List, 然后填充好 Map .

Person.java
 1class Person {
 2
 3  public final String name;
 4  public final String gender;
 5
 6  public Person(String name, String gender) {
 7    this.name = name;
 8    this.gender = gender;
 9  }
10
11  @Override
12  public String toString() {
13    return name;
14  }
15}

Java 既然提供了 java.util.stream.Collector 接口让我们扩展,那么想要什么样的 Collector 就自己创建吧。 永久链接 https://yanbin.blog/java-8-groupingby-empty-map-group/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。