欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java 8如何自定义收集器(collector)详解

程序员文章站 2023-12-05 20:05:34
需求: 将 一个容器list 按照一定的字段进行分组,分组过后的值为特定的bean 里面的属性例如: 假定有这样一个bean...

需求:

将 一个容器list<bean> 按照一定的字段进行分组,分组过后的值为特定的bean 里面的属性例如:

假定有这样一个bean

 public class subjectoberser{
  private string subjectkey;
  private abstractobserver abstractobserver;
  ...geter seter 方法...
 } 

我们需要按照 subjectkey 进行分组,分组过后的内容 应该为这样一个容器map<string,list<abstractobserver>>

map 中的key,为subjectoberser 属性的subjectkey,值为list<abstractobserver>

实现过程

首先来看看collector 的接口定义

 public interface collector<t, a, r> { 
   supplier<a> supplier();
   biconsumer<a, t> accumulator();
   function<a, r> finisher();
   binaryoperator<a> combiner();
   set<characteristics> characteristics();
 }

类型 t ,是在容器里面元素的类型

类型 a ,是accumulator 返回的类型,即是累加器的返回类型

类型 r ,是最终结果的类型

supplier 方法返回的结果必须为一个空的supplier,也就是一个空的无参函数(签名就是这样的 ()->{}),在调用的时候它会创建一个空的累加器(accumulator)实例,供数据收集的时候使用,很明显如果按照我们的需求试下你自己collector 这里应该返回一个 () -> new hashmap<>() ,一个map 来收集结果

accumulator 方法返回归约操作的函数(签名是这样的 (a,b)->void ),当遍历到流中第n个元素时,这个函数执行时会有两个参数:保存归约结果的累加器(已 收集了流中的前n-1个项目),还有第n个元素本身。签名也展示该函数是void,因为该操作是在原来的容器里面进行更新的,所以返回的是void 类型。按照需求的中的实现应该是是这样的:

 public biconsumer<map<string, list<abstractobserver>>, subjectobserver> accumulator() {
  return (map<string, list<abstractobserver>> acc, subjectobserver v) -> {
   if (acc.containskey(v.getsubjectkey())){
    acc.get(v.getsubjectkey()).add(v.getabstractobserver());
   }else{
    list<abstractobserver> l = new arraylist<>();
    l.add(v.getabstractobserver());
    acc.put(v.getsubjectkey(),l);
   }
  };
 }

这里的逻辑就是if else  逻辑判断就是,这个key ,在map 中是否存在,如果不存在,那么我们需要给他new一个list 的实例,不然我的的数据没有地方存储

finisher 可从名字看出方法累积过程的最后要调用的一个函数,以便将累加器对象转换为整个集合操作的最终结果。通常来说累加器的类型也是返回的结果的类型,那么就返回identity 就可以了,如果不是的话,就行自行转换了。在当前需求的情况下我们的累加器和返回结果的类型是一致的,所以这里的实现是这样的:

 public function<map<string, list<abstractobserver>>,
     map<string, list<abstractobserver>>> finisher(){
   return function.identity();
 }

combiner 方法是将两个累加的结果进行一个合并的过程,当然这个过程并不是每一个collector 都会调用得到(后面会讲到)
按照我们的需求,只需要将两个累加器的,中间结果合并成为一个结果即可,所以是现实这样的:

  public binaryoperator<map<string, list<abstractobserver>>> combiner() {
  return ((map<string, list<abstractobserver>> map1, 
    map<string, list<abstractobserver>> map2) -> {
     map1.putall(map2);
     return map1;
   });
 }

characteristics 该方法返回一个 characteristics 的集合,它有如下值可选

    unordered—— 归约结果不受流中项目的遍历和累积顺序的影响。

    concurrent—— accumulator函数可以从多个线程同时调用,且该收集器可以并行执行。如果收集器没有标为unordered,那 它仅在用于用于无序数据源时才可以并行归约。

    identity_ finish—— 这表明完成器方法返回的函数是一个不改变的函数,这种情况下,累加器对象将会直接用作合并过程 的最终结果。

   public set<characteristics> characteristics() {
    return collections.unmodifiableset(enumset.of(characteristics.identity_finish));
   }

最终collector 代码合在一起就是:

 public class mycollector implements collector<subjectobserver,
  map<string, list<abstractobserver>>,
  map<string, list<abstractobserver>>> {


  @override
  public supplier<map<string, list<abstractobserver>>> supplier() {
   return () -> new hashmap<>();
  }

  @override
  public biconsumer<map<string, list<abstractobserver>>, subjectobserver> accumulator() {
   return (map<string, list<abstractobserver>> acc, subjectobserver v) -> {
    if (acc.containskey(v.getsubjectkey())) {
     acc.get(v.getsubjectkey()).add(v.getabstractobserver());
    } else {
     list<abstractobserver> l = new arraylist<>();
     l.add(v.getabstractobserver());
     acc.put(v.getsubjectkey(), l);
    }
   };
  }

  @override
  public binaryoperator<map<string, list<abstractobserver>>> combiner() {
   return ((map<string, list<abstractobserver>> map1, map<string, list<abstractobserver>> map2) -> {
    map1.putall(map2);
    return map1;
   });
  }

  @override
  public function<map<string, list<abstractobserver>>, map<string, list<abstractobserver>>> finisher() {
   return function.identity();
  }

  @override
  public set<characteristics> characteristics() {
   return collections.unmodifiableset(enumset.of(characteristics.identity_finish));
  }
 }

调用的过程就是:

 public static map<string, list<abstractobserver>> initobjectmap() {
 classscaner classscaner = new classscaner();
 set<class> set = classscaner.doscan("com.souche.datacenter.observer");
 return set
   .stream()
   .filter(aclass -> subjectannotationresolver.getannotationsubjectname(aclass) != null)
   .map(aclass -> {
    string subjectkey = subjectannotationresolver.getannotationsubjectname(aclass);
    abstractobserver abstractobserver = getbeanbyclassname(aclass.getsimplename());
    return new subjectobserver(subjectkey, abstractobserver);
   }).collect(new mycollector());
 }

直接在使用的地方直接new mycollector 就可以了

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。