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

作者:chauncy 时间:2022-02-12 07:22:17 

需求:

将 一个容器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 就可以了

总结

标签:java8,collector,收集器
0
投稿

猜你喜欢

  • SSM Mapper文件查询出返回数据查不到个别字段的问题

    2023-10-17 01:02:08
  • Java如何实现自定义异常类

    2023-06-21 23:44:01
  • C#多线程之任务的用法详解

    2023-08-27 10:51:18
  • c# 颜色选择控件的实现代码

    2022-04-27 07:22:58
  • 深入理解Java IO的flush

    2022-01-02 20:05:53
  • JAVA中数组从小到大排序的2种方法实例

    2021-10-09 09:46:18
  • 详解OAuth2 Token 一定要放在请求头中吗

    2022-05-01 09:43:15
  • SpringBoot2零基础到精通之映射与常用注解请求处理

    2022-06-11 15:41:51
  • springcloud LogBack日志使用详解

    2021-12-23 15:17:05
  • Java实现批量向mysql写入数据的方法

    2023-11-05 20:51:21
  • 浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别

    2022-05-27 00:37:31
  • java随机生成8位数授权码的实例

    2022-04-24 12:03:47
  • 详细理解JAVA面向对象的封装,继承,多态,抽象

    2023-07-05 06:54:14
  • springboot之端口设置和contextpath的配置方式

    2023-10-05 14:16:20
  • sin(x)如何求解的java代码实现方法

    2022-04-17 16:22:07
  • java使用ftp上传文件示例分享

    2021-10-23 08:33:03
  • SpringBoot 开发提速神器 Lombok+MybatisPlus+SwaggerUI

    2022-07-08 07:40:23
  • Spring AOP面向切面编程实现原理方法详解

    2021-07-22 00:26:07
  • C# Dynamic之:ExpandoObject,DynamicObject,DynamicMetaOb的应用(上)

    2022-11-02 06:21:42
  • Unity实现仿3D轮转图效果

    2023-11-24 12:26:56
  • asp之家 软件编程 m.aspxhome.com