当前位置: 首页 > 后端技术 > Java

Java流高级使用之收集数据分析

时间:2023-04-01 14:48:31 Java

目录1.前言1.1收集器1.2预定义收集器Collectors类为我们提供了一个收集器,主要包括三个功能:2.深入2.1统计汇总查找数ofelementsstream中的最大值和最小值汇总连接字符串2.2分组1.前言1.1Collector收集器的接口是java.util.stream.Collector,我们只需要调用stream的collect方法并通过到Collector接口的一个实现(也就是汇总Stream中的元素的方法),就是这样。例如,java.util.stream.Collectors类的toList()方法返回Collector接口的实现,该接口按顺序为每个元素生成一个列表。收集器很有用,因为它们允许您简洁灵活地定义collect用于生成结果集的条件。更具体地说,调用流上的collect方法将触发流中元素的归约操作(由Collector参数化)。1.2预定义收集器JDK为我们提供了java.util.stream.Collectors类,它为我们提供了很多静态工厂方法,可以方便的创建普通的收集器实例,我们只需要使用即可。最直接和常用的收集器是toList静态方法,它将流中的所有元素收集到一个List中。Collectors类提供的收集器主要包括三大功能:将流元素归约汇总为值元素分组元素分区注:因为它为我们提供了静态方法,我们可以通过静态导入的方式来简化代码的编写。2.深入2.1元素个数的法定汇总统计Collectors为我们提供了一种统计方法和一个统计元素个数的收集器。示例:longhowManyDishes=menu.stream().collect(Collectors.counting());上面的例子是使用预定义的收集器实现的。其实Stream接口定义了count方法,所以我们也可以直接调用Stream提供的预设定义方法来实现,如下:longhowManyDishes=menu.stream().count();哪一个应该用于相同的功能?事实上,如果你的需求只是统计流中元素的个数,两者都可以。最大的区别是count()是一个终端操作,而计数返回一个可以与其他收集器结合使用的收集器。查找流中的最大值和最小值Collectors为我们提供了maxBy方法和mixBy方法,它们为我们提供了计算流中最大值或最小值的Collectors。示例:ComparatordishCaloriesComparator=Comparator.comparingInt(Dish::getCalories);可选mostCalorieDish=menu.stream().collect(maxBy(dishCaloriesComparator));SummarizationCollectors类提供了一个专门用于汇总的工厂方法:Collectors.summingInt。它接受一个将对象映射到求和所需的整数的函数,并返回一个收集器;这个收集器,当传递给普通的收集方法时,执行我们需要的聚合。Collectors.summingLong类和Collectors.summingDouble类的功能完全一样,都可以在求和字段为long或double时使用。查找菜单列表总卡路里的示例:inttotalCalories=menu.stream().collect(summingInt(Dish::getCalories));上述代码的采集过程如下图所示。遍历流时,每道菜都映射到它的卡路里,并将这个数字添加到累加器(这里的初始值为0)。averageCollectors类的averagingInt、averagingLong和averagingDouble可以为我们生成一个计算平均数的收集器:doubleavgCalories=menu.stream().collect(averagingInt(Dish::getCalories));Collectors类为我们提供了summarizingInt工厂方法,它返回的收集器可以一次性统计总和、平均值、最大值和最小值。例如,通过一个汇总操作,您可以计算菜单中元素的数量并获得菜肴的总和、平均、最大和最小卡路里:IntSummaryStatisticsmenuStatistics=menu.stream().collect(summarizingInt(Dish::getCalories));该收集器会将所有这些信息收集到一个名为IntSummaryStatistics的类中,该类提供了一种方便的值方法来访问结果。打印menuStatistic对象得到如下输出:IntSummaryStatistics{count=9,sum=4300,min=120,average=477.777778,max=800}同样,对应的summarizingLong和summarizingDouble工厂方法关联了LongSummaryStatistics和DoubleSummaryStatistics类型,适合收集属性是原始类型long或double的情况。Collectors类提供的join工厂方法返回的收集器会将流中每个对象应用toString方法得到的所有字符串拼接成一个字符串。这意味着您可以像这样连接菜单中所有菜肴的名称:StringshortMenu=menu.stream().map(Dish::getName).collect(joining());注意:加入内部使用了一个StringBuilder来将生成的字符串一个一个追加。此外,joining工厂方法有一个重载,它接受元素之间的分隔符,因此您可以获得以逗号分隔的菜名列表:StringshortMenu=menu.stream().map(Dish::getName).collect(joining(","));广义归约总结上面提到的收集器都是归约过程的特例,可以用归约工厂方法定义。Collectors.reducing工厂方法是所有这些特殊情况的概括。publicstaticCollectorreducing(Uidentity,Functionmapper,BinaryOperatorop)参数分析:第一个参数是归约的开始operation初始值,也是流中没有元素时的返回值。第二个参数是Function,会做一定的转换操作。第三个参数BinaryOperator将两项累加为同一类型的值。让我们来转换上面的例子:=menu.stream().collect(reducing((d1,d2)->d1.getCalories()>d1.getCalories()?d1:d2));在上面的转换例子中,我们使用单参数reducing工厂方法创建的收集器,可以看作是三参数方法的一个特例,它以流中的第一项为起点,恒等函数(也就是说,一个简单地返回其输入参数的函数)作为转换函数。2.2分组一个常见的数据库操作是根据一个或多个属性对集合中的项进行分组。假设您想按类型对菜单中的菜肴进行分类,将肉归为一组,将鱼归为一组,将其他所有东西归为另一组。这可以通过Collectors.groupingBy工厂方法返回的Collectors轻松完成,如下所示:Map>dishesByType=menu.stream().collect(groupingBy(Dish::getType));在这里,您将一个函数(以方法引用的形式)传递给groupingBy方法,该方法提取流中每个Dish的Dish.Type。我们称这个Function为分类函数,因为它是用来将流中的元素分成不同的组。如下图所示,分组操作的结果是一个Map。分组函数返回的值作为map的key,stream中所有具有该分类值的item的列表作为对应的map值。在菜单类别示例中,键是菜肴的类型,值是包含该类型所有菜肴的列表。特殊应用示例:publicenumCaloricLevel{DIET,NORMAL,FAT}Map>dishesByCaloricLevel=menu.stream().collect(groupingBy(dish->{if(dish.getCalories()<=400)returnCaloricLevel.DIET;elseif(dish.getCalories()<=700)返回CaloricLevel.NORMAL;否则返回CaloricLevel.FAT;}));以上是个人经验,希望能给大家一个参考