当前位置: 首页 > 科技观察

Java12Collectors.teeing你真的需要了解

时间:2023-03-13 01:01:36 科技观察

前言在Java12中有一个非常有用的功能,但是在官方的JEP中并没有公布,因为它只是Collector的一个小改动,它的作用就是合并两个收集器的结果。这句话看起来很抽象,也很老土。先来看一张图:这个小东西在管道改造中经常用到。通常我们称之为“三通”。它的主要作用是将downstream1和downstream2的流入进行合并,再从合并流出。有了这个图片描述,我们就可以进入正题了。上面提到的Collectors.teeing中的小函数就是Collectors.teeingAPI。首先我们看一下JDK对API的描述。如果你觉得不舒服,就忽略它并继续看例子:/***返回一个{@codeCollector},它是两个下游收集器的组合。*传递给结果收集器的每个元素都由两个下游*收集器处理,然后它们的结果使用指定的合并函数*合并到最终结果中。**

结果收集器函数执行以下操作:**

    *
  • 供应商:创建包含结果容器*的结果容器*通过调用每个收集器的供应商*
  • 累加器:调用每个收集器的累加器及其结果容器*和输入元素*
  • 组合器:调用每个收集器的带有两个结果容器的组合器*
  • 完成器:调用每个收集器的完成器及其结果容器,*然后调用thesuppliedmergerandreturnsitsresult.*
**

结果collectoris{@linkCollector.Characteristics#UNORDERED}ifbothdownstream*collectorsareunorderedand{@linkCollector.Characteristics#CONCURRENT}ifbothdownstream*collectorsareconcurrent.**@paramthetypeoftheinputelements*@param第一个收集器的结果类型*@param第二个收集器的结果类型*@param最终结果类型*@paramdownstream1thefirstdownstreamcollector*@paramdownstream2theseconddownstreamcollector*@parammergerthefunctionwhichmergestworesultsintothesingleone*@returna{@codeCollector}whichaggregatesultsoftwosuppliedcollectors,*/Rcolteeing(Collectordownstream1,Collectordownstream2,BiFunctionmerger){returnteeing0(downstream1,downstream2,merger);}API描述重的一句话非常关键:传递给结果收集器的每个元素都是p由两个下游收集器处理结合“三向图”说明集合中每个要传递给合并的元素都会被downstream1和downstream2处理。合并的类型是BiFunction,也就是说它接收两个参数,输出一个值,请看它的apply方法@FunctionalInterfacepublicinterfaceBiFunction{/***Appliesthisfunctiontothegivenarguments.**@paramtthefirstfunctionargument*@paramuthescondfunctionargument*@returnthefunctionresult*/Rapply(Tt,Uu);}至于如何处理,我们来看一些例子。为了更好的说明teeing的使用,列举了四个例子。看完这四个例子,再回头看看上面的API说明。我相信你会首先了解计数和累加。我们来看一个经典的。问题,给定一个数字集合,你需要映射整数流中的元素个数和它们的总和OverridepublicStringtoString(){return"CountSum{"+"count="+count+",sum="+sum+'}';}}由Collectors.teeing处理CountSumcountsum=Stream.of(2,11,1,5,7,8,12).collect(Collectors.teeing(counting(),summingInt(e->e),CountSum::new));System.out.println(countsum.toString());downstream1是通过Collectors的静态方法进行计数集合计数downstream2通过CollectorsMerger的静态方法summingInt累加集合元素值通过CountSum构造函数收集结果运行结果:CountSum{count=7,sum=46}我们通过teeing一次性得到我们想要的结果,继续往下看其他例子:最大值和最小值通过给定集合,一次性计算集合的最大值和最小值,新建类MinMax,创建合并的构造函数收集结果classMinMax{privatefinalIntegermin;privatefinalIntegermax;publicMinMax(Integermin,Integermax){this.min=min;this.max=max;}@OverridepublicStringtoString(){return"MinMax{"+"min="+min+",max="+max+'}';}}通过teeingAPI计算结果:MinMaxminmax=Stream.of(2,11,1,5,7,8,12).collect(Collectors.teeing(minBy(Comparator.naturalOrder()),maxBy(Comparator.naturalOrder()),(Optionala,Optionalb)->newMinMax(a.orElse(Integer.MIN_VALUE),b.orElse(Integer.MAX_VALUE))));System.out.println(minmax.toString());downstream1通过Collectors的静态方法minBy,通过Comparator的自然排序找到downstream2的最小值。通过Collectors的静态方法maxBy,通过Comparator的自然排序找到最大值。双函数的两个输入参数经过Optional处理:MinMax{min=1,max=12}为了验证Optional,我们在集合中添加一个null元素,修改排序规则,查看排序结果:MinMaxminmax=Stream.of(null,2,11,1,5,7,8,12).collect(Collectors.teeing(minBy(Comparator.nullsFirst(Comparator.naturalOrder())),maxBy(Comparator.nullsLast(Comparator.naturalOrder())),(可选<整数>a,可选<整数>b)->newMinMax(a.orElse(Integer.MIN_VALUE),b.orElse(Integer.MAX_VALUE))));downstream1处理规则是把null放在排序最前面的downstream2处理规则是把null放在排序的最后。合并处理时,会执行optional.orElse方法,分别输出最小和最大运行结果:MinMax{min=-2147483648,max=2147483647}瓜的总重量和单个重量下面举个例子一个比较实用的操作对象//定义瓜的类型和权重){returnweight;}}//总重量和个体重量列表classWeightsAndTotal{privatefinalinttotalWeight;privatefinalListweights;publicWeightsAndTotal(inttotalWeight,Listweights){this.totalWeight=totalWeight;this.weights=weights;}@OverridepublicStringtoString(){return"WeightsAndTotal{"+"totalWeight="+totalWeight+",weights="+weights+'}';}}通过teeingAPI计算总重量和单单重量Listmelons=Arrays.asList(newMelon("Crenshaw",1200),newMelon("Gac",3000),newMelon("Hemi",2600),newMelon("Hemi",1600),newMelon("Gac",1200),newMelon("Apollo",2600),newMelon("Horned",1700),newMelon("Gac",3000),newMelon("Hemi",2600));WeightsAndTotalweightsAndTotal=melons.stream().collect(Collectors.teeing(summingInt(Melon::getWeight),mapping(m->m.getWeight(),toList()),WeightsAndTotal::new));System.out.println(weightsAndTotal).toString());downstream1使用Collectors的静态方法summingInt做权重累加downstream2使用Collectors的静态方法mapping提取瓜的权重,通过流终止操作toList()Merger获取结果,通过WeightsAndTotal构造函数获取结果:WeightsAndTotal{totalWeiGHT=19500,重量=[1200,3000,2600,1600,1200,2600,1700,1700,3000,2600]}}继续继续一个个贴合贴合的实际的的例子例子的:预约:预约publicGuest(Stringname,booleanparticipating,IntegerparticipantsNumber){this.name=name;this.participating=participating;this.participantsNumber=participantsNumber;}publicbooleanisParticipating(){returnparticipating;}publicIntegergetParticipantsNumber(){returnparticipantsNumber;}publicStringgetName(){returnname;}}classEventParticipation{privateListguestNameList;privateIntegertotalNumberOfParticipants;publicEventParticipation(ListguestNameList,IntegertotalNumberOfParticipants){this.guestNameList=guestNameList;this.totalNumberOfParticipants=totalNumberOfParticipants;}@OverridepublicStringtoString(){return="+Event"参与guestNameList+",totalnumberofparticipants="+totalNumberOfParticipants+"}";}}通过teeingAPI处理varresult=Stream.of(newGuest("Marco",true,3),newGuest("David",false,2),newGuest("Roger",true,6)).collect(Collectors.teeing(Collectors.filtering(Guest::isParticipating,Collectors.mapping(Guest::getName,Collectors.toList())),Collectors.summingInt(Guest::getParticipantsNumber),EventParticipation::new));System.out.println(结果);downstream1通过filter的方式过滤出确认的参与者,并映射出他们的名字,最后放入toList集合中。downstream2使用summingInt方法对合并进行计数和累加。通过EventParticipation构造函数收集结果。我们定义varresult为集合结果没有指定类型。这种语法糖也加快了我们编程的效率。运行结果:EventParticipation{guests=[Marco,Roger],totalnumberofparticipants=11}综上所述,teeingAPI就是灵活应用Collectors中定义的静态方法。收集元素经过downstream1和downstream2处理,最后通过merge收集。当项目中同时有两个采集结果时,就可以应用我们的teeingAPI了。