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.getGender)));

//输出:{male=[Tom, Jerry]}

Stream<Person> stream = Stream.of(new Person("Tom", "female"), new Person("Jerry", "male"));
System.out.println(stream.collect(Collectors.groupingBy(person -> person.getGender)));
//输出:{female=[tom], male=[Jerry]}

而无法表示存在其他 gender 的可能性,并且 female=[] 的情况,即想要结果

{male=[Tom, Jerry], female=[]}

如果想得到以上的结果该当如何呢? stream.collect() 接受一个 Collector, Collectors 中只是定义了许多常用的 Collector 实现,如果不够用的话我们可以实现自己的 Collector. 下面就来定义一个 GroupingWithKeys, 它需要实现 java.util.stream.Collector 接口,有五个接口方法. 事成之后我们写

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
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
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://my.oschina.net/u/2391658/blog/872690

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值