写这个文章其实主要是因为刚有个童鞋问了个问题https://segmentfault.com/q/10...

正写的带劲安利Java8的实现方式,结果还没写完...无意发现问题被关闭了...哎...都写了一半了...又不想放弃,就干脆写成文章

问题主要就是把集合里的数据按照一定大小顺序平均分成若干组的问题,看起来挺简单的,不过我开始看到就想用用stream来实现,但是想了想Collectors里并没有适合的方法,所以就想到了用定制的collector来实现了。

原问题的截图:

正式开始回答(我是直接把之前的回答copy过来的哈):

集合处理的话,我还是推荐Java8的stream,题主这个问题设计到分组,那自然就要涉及到stream的collect方法了,这个方法是收集数据的意思,该方法的参数就是一个Collector接口,只要传入一个Collector的实现类就可以了,常用的实现比如在工具类Collectors里有toList,toMap等,已经帮你默认写了收集为集合或者Map的实现类了,但是明显这些实现类都不合适,所以这里需要定制一个Collector接口的实现啦

其实就是仿照Collectors里的内部类CollectorImpl写一个就是了...

=====================(Collector介绍,如果你已经清楚可以略过的...)==================

介绍哈Collector接口的方法,一共5个

Supplier supplier()

BiConsumer accumulator()

BinaryOperator combiner()

Function finisher()

Set characteristics()

方法中有泛型,所以要先要介绍哈Collector中的三个泛型T, A, R

T:stream在调用collect方法收集前的数据类型

A:A是T的累加器,遍历T的时候,会把T按照一定的方式添加到A中,换句话说就是把一些T通过一种方式变成A

R:R可以看成是A的累加器,是最终的结果,是把A汇聚之后的数据类型,换句话说就是把一些A通过一种方式变成R

了解了泛型的意思,咱们结合Collectors.toList构造的默认实现类的实现方式来看看Collector接口的方法

public static

Collector> toList() {

return new CollectorImpl<>((Supplier>) ArrayList::new, List::add,

(left, right) -> { left.addAll(right); return left; },

CH_ID);

}

官方写的很简单,很随意...

前三个参数分别对应了Collector的前三个方法,也就是

(Supplier>) ArrayList::new 对应Supplier supplier()第一个方法

List::add 对应BiConsumer accumulator()第二个方法

(left, right) -> { left.addAll(right); return left; }对应BinaryOperator combiner()第三个方法

所以对应着来看就清楚了

Supplier supplier() 怎么创建一个累加器(这里对应的是如何创建一个List)

BiConsumer accumulator()怎么把一个对象添加到累加器中(这里对应的是如何在List里添加一个对象,当然是调用add方法咯)

BinaryOperator combiner()怎么把一个累加器和另一个累加器合并起来(这里对应的是如何把List和List合并起来,当然是调用addAll,这里由于最终要返回List,所以A和R是一个类型,都是List所以才调用addAll)

再来看看第四个方法Function finisher(),其实就是怎么把A转化为R,由于是toList,所以A和R是一样的类型,这里其实用就是Function.identity

最后第五个方法Set characteristics()其实就是这个Collector的一些性质,toList这里只用了Characteristics.IDENTITY_FINISH,表示第四个方法可以不用设置,A类型就是最终的结果

=====================(Collector介绍完了)==================

现在创建自定义的collector,类名我就叫NumberCollectorImpl,由于collector这里要求有三个泛型,根据题主的需求,这三个泛型只有第一个是未知的,另外两个应该是确认的List结构,所以写出来应该是这么个效果

static class NumberCollectorImpl implements Collector>, List>>

ok,针对collector要求实现的5个方法来依次说明

第一个方法Supplier>> supplier(),很明显应该就是ArrayList::new

第二个方法BiConsumer>, T>,这个稍微麻烦点,起始应该写成(list, item) -> {},主要就是补充{}中的代码了

最开始的遍历的时候,这个list其实是父list,它肯定是空的,所以这个时候要创建一个新子List,然后把item塞进子list中,最后再把创建的新子list放入到父list中

if (list.isEmpty()){

list.add(this.createNewList(item));

}

这里简单封了一个小方法createNewList,因为待会还要用

private List createNewList(T item){

List newOne = new ArrayList();

newOne.add(item);

return newOne;

}

若父list不为空,那就要把当前父list中最后一个子list取出来,若空的话,当然又是要创建一个新子list然后按照之前的方法做,若不为空,就判断子list大小咯,若大小超过2,就再次创建一个新子list然后塞item,若没有超过就在之前子list中塞入item,写出来大概就是这个样子

List last = (List) list.get(list.size() - 1);

if (last.size() < 2){

last.add(item);

}else{

list.add(this.createNewList(item));

}

第三个方法BinaryOperator>> combiner(),其实就是两个List如何合并,当然是addAll方法

(list1, list2) -> {

list1.addAll(list2);

return list1;

};

第四个方法Function>, List>> finisher(),由于这个时候A和R的类型一样,都是List>,所以这里直接就是Function.identity()啦

最后一个方法Set characteristics()这里直接可以按照Collectors.toList来弄就行了,也就是直接采用Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH))

综上所述,完整代码如下

/**

* 自定义Collector

*

* @author imango

* @since 2017/7/13

*/

public class CustomCollectors {

// 默认采用2个一起分组

public static Collector>, List>> groupByNumber(){

return CustomCollectors.groupByNumber(2);

}

// 根据number的大小进行分组

public static Collector>, List>> groupByNumber(int number){

return new NumberCollectorImpl(number);

}

/**

* 个数分组器

* @param

*/

static class NumberCollectorImpl implements Collector>, List>> {

// 每组的个数

private int number;

public NumberCollectorImpl(int number) {

this.number = number;

}

@Override

public Supplier>> supplier() {

return ArrayList::new;

}

@Override

public BiConsumer>, T> accumulator() {

return (list, item) -> {

if (list.isEmpty()){

list.add(this.createNewList(item));

}else {

List last = (List) list.get(list.size() - 1);

if (last.size() < number){

last.add(item);

}else{

list.add(this.createNewList(item));

}

}

};

}

@Override

public BinaryOperator>> combiner() {

return (list1, list2) -> {

list1.addAll(list2);

return list1;

};

}

@Override

public Function>, List>> finisher() {

return Function.identity();

}

@Override

public Set characteristics() {

return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));

}

private List createNewList(T item){

List newOne = new ArrayList();

newOne.add(item);

return newOne;

}

}

}

外面那个类CustomCollectors 主要是为了封装NumberCollectorImpl类,以后也可以把其他自定义的收集器实现放在这里面,并对外提供工具方法,并且我在NumberCollectorImpl类中新增了一个number成员变量,这样就可以自定义分组大小了,CustomCollectors提供了两个对外方法groupByNumber,带参数的那个就是可以自定义分组个数的了,没有参数的就是默认按照2个分组了,这样的话,测试写法就是这样

public static void main(String[] args) {

List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 按照2个分组

List> twoNumberList = list.stream().collect(CustomCollectors.groupByNumber());

// 按照5个分组

List> fiveNumberList = list.stream().collect(CustomCollectors.groupByNumber(5));

}

这样代码就非常漂亮了~哈哈哈~~

java8接口可以实现方法目的_Java8 collector接口的定制实现相关推荐

  1. 接口中默认方法和静态方法_接口中的默认方法和静态方法

    接口中默认方法和静态方法 在我们最初的Java 8支持公告中,我们特别提到了流的缺乏,但完全错过了默认/静态本机接口不起作用的事实. 现在,由于有一个警惕的社区成员指出了这一问题,因此此问题已得到解决 ...

  2. php怎么给接口里的方法传参,PHP接口中方法的参数和实现类方法中的参数可以不一致的问题...

    interface Dispatch2 { public function getController(); } class QueryString implements Dispatch2 { pu ...

  3. java流式编程(六)Collector接口

    目录 一.接口定义 二.接口泛型 一.接口定义 public interface Collector<T, A, R> {Supplier<A> supplier();BiCo ...

  4. ArcGIS 网络分析[8.2] 资料2 使用IDatasetContainer2接口的CreateDataset方法创建网络数据集...

    上节提及如何使用IDatasetContainer2接口访问到网络数据集,上例可以封装为一个方法. 这节就使用IDatasetContainer2接口(Geodatabase类库)的CreateDat ...

  5. 继承父类 , 实现接口 时, 方法的权限

    在说这个问题之前,先简单介绍一下接口中的方法的访问权限 接口中所有的方法,默认都是 public abstract 所有的属性,默认都是public static final 接口中定义不了,访问权限 ...

  6. Java8新特性之- Lambda表达式和函数式接口

    Lambda表达式和函数式接口 1. 背景 Java是一门面向对象编程语言.面向对象编程语言和函数式编程语言中的基本元素(Basic Values)都可以动态封装程序行为:面向对象编程语言使用带有方法 ...

  7. 接口、工厂方法的设计模式、代理模式

    三.接口: 接口是定义的一种功能:interface关键字表明是一个接口,定义的是一个功能.这些功能也可以被类使用,表明的是此接口与这些类发生的关系. implements表明类实现接口,实现接口以后 ...

  8. java的cloneable_Java的Cloneable接口和clone方法

    1. 克隆的用处 在日常编码中我们经常需要产生某个对象的副本,这里的副本并不是指向同一个对象的不同引用,而是与当前对象状态一模一样的另一个新的对象.如果使用单纯的引用赋值,会发生什么效果呢? 我们可以 ...

  9. java 接口工程_Java工程师(15)抽象类与接口

    抽象类 思考下面程序潜在的问题 交通工具中定义了4个方法,其中行驶方法内部会依次调用启动.加速.停止方法.由于不同的交通工具,启动的方式差异很大,所以交通工具类中并不实现该方法,而是将其交给子类实现. ...

  10. 接口不能被实例化的吗?接口引用是什么?

    接口,在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法. 接口并不是类,编写接口的方式和类很相似,但是它们属 ...

最新文章

  1. java 线程池ThreadPoolExecutor
  2. 第3周实践项目7 删除链表元素最大值
  3. 利用Nginx做负载均衡
  4. 自新建表包含货币金额类型和数量类型时报错问题。
  5. 排队接水pascal程序
  6. 从入门到进阶|如何基于WebRTC搭建一个视频会议
  7. spring框架学习笔记(八)
  8. 微博安全是一个系统问题包括服务器安全,应用安全开发注意事项
  9. POJ 1321 棋盘问题(回溯)
  10. php 构造函数 返回值,php构造函数与析构函数实例分析
  11. kex_exchange_identification: Connection closed by remote host Connection closed by 140.82.121.3 port
  12. 启动kafka报错:__consumer_offsets-22\00000000000000000000.index.swap: 另一个程序正在使用此文件,进程无法访问。
  13. 更新K3官改里面都FRP到0.20版本的方法
  14. 谷歌、亚马逊在区块链都耽搁了什么 竟让后起的脸书名声大噪
  15. 创建套接字socket函数的详解(sock_stream和sock_dgram的分析)
  16. My Thirty-eighth - 验证二叉树搜索树 - By Nicolas
  17. 红帽linux5.4安装教程,红帽企业Linux5.4下ORACLE安装步骤(推荐).pdf
  18. matlab画柱状图函数,series函数做柱状图
  19. 面料软件_面料管理系统_面料订单管理
  20. IDEA编码小技巧(鼠标光标等等)

热门文章

  1. oracle安装蓝屏_Windows下安装ORACLE RAC蓝屏无限重启
  2. 广义pareto分布_Generalized Pareto Distribution (GPD)
  3. 公安交管网服务器维护,交管网总是维护
  4. [译]利用贝叶斯推理做硬件故障率的准实时预测
  5. php使用到的函数记录一
  6. 菜鸟学Linux 第048篇笔记 配置slave server
  7. 虚拟ONVIF 摄像机
  8. 获取synchronized锁中的阻塞队列中的线程是非公平的
  9. css background背景拉伸
  10. 中文的在一个字符串中查找另一个字符