关于java:Java-流的高级使用之收集数据解析

5次阅读

共计 3912 个字符,预计需要花费 10 分钟才能阅读完成。

目录
一、前言
1.1 收集器
1.2 预约义收集器
Collectors 类为咱们提供的收集器,次要蕴含三大性能:
二、深刻
2.1 规约和汇总
统计元素数量
查找流中的最大值和最小值
汇总
连贯字符串
2.2 分组
一、前言
1.1 收集器
收集器的接口是 java.util.stream.Collector,咱们只须要调用流的 collect 办法并传递给一个 Collector 接口的一个实现 (也就是给 Stream 中元素做汇总的办法),就能够了。例如 java.util.stream.Collectors 类的 toList() 办法,该办法就会返回一个按程序给每个元素生成一个列表的 Collector 接口的实现。

收集器十分有用,因为它能够简介而灵便地定义 collect 用来生成后果汇合的规范。更具体地说,对流调用 collect 办法将对流中的元素触发一个规约操作(由 Collector 来参数化)。

1.2 预约义收集器
JDK 为咱们提供了 java.util.stream.Collectors 类,其为咱们提供了很多动态工厂办法,能够不便地创立常见的收集器实例,而咱们只有拿来用就能够了。最间接和最罕用的收集器是 toList 静态方法,它会把流中所有的元素收集到一个 List 中。

Collectors 类为咱们提供的收集器,次要蕴含三大性能:
将流元素规约和汇总为一个值
元素分组
元素分区
留神:因为其为咱们提供的都是静态方法,咱们能够通过动态导入的形式简化代码的书写。

二、深刻
2.1 规约和汇总
统计元素数量
Collectors 为咱们提供了 counting 办法,为咱们提供了统计元素数量的收集器。实例:

long howManyDishes = menu.stream().collect(Collectors.counting());

下面的示例是利用预约义收集器实现的,其实 Stream 接口定义了 count 办法,因而咱们也能够间接调用 Stream 提供的预定义方法来实现,如下:

long howManyDishes = menu.stream().count();

性能一样用哪个才好呢?其实如果你的需要只是统计流中元素的数量的时候,二者皆可。最大的区别在于 count()是一个终端操作,而 counting 返回的是一个收集器,其能够和其它收集器联结应用。

查找流中的最大值和最小值
Collectors 为咱们提供了 maxBy 办法和 mixBy 办法,为咱们提供了计算流中的最大或最小值的收集器。实例:

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));

汇总
求和

Collectors 类专门为汇总提供了一个工厂办法:Collectors.summingInt。它可承受一个把对象映射为求和所需 int 的函数,并返回一个收集器;该收集器在传递给一般的 collect 办法后即执行咱们须要的汇总操作。

类 Collectors.summingLong 和 Collectors.summingDouble 办法的作用齐全一样,能够用于求和字段为 long 或 double 的状况。

求出菜单列表的总热量的示例:

int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

下面代码的收集过程下图所示。在遍历流时,会把每一道菜都映射为其热量,而后把这个数字累加到一个累加器(这里的初始值 0)。

平均值

Collectors 类的 averagingInt、averagingLong 和 averagingDouble 能够为咱们生成计算数值的平均数的收集器:

double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories));

Collectors 类为咱们提供了 summarizingInt 工厂办法,其返回的收集器能够一次性统计出总数、总和、平均值、最大值和最小值。

例如,通过一次 summarizing 操作你能够就数出菜单中元素的个数,并失去菜肴热量总和、平均值、最大值和最小值:

IntSummaryStatistics menuStatistics =
        menu.stream().collect(summarizingInt(Dish::getCalories));

这个收集器会把所有这些信息收集到一个叫作 IntSummaryStatistics 的类里,它提供了不便的取值办法来拜访后果。打印 menuStatisticobject 会失去以下输入:

IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}

同样,相应的 summarizingLong 和 summarizingDouble 工厂办法有相干的 LongSummaryStatistics 和 DoubleSummaryStatistics 类 型,实用于收集的属性是原始类型 long 或 double 的状况。

连贯字符串
Collectors 类为咱们提供的 joining 工厂办法返回的收集器会把对流中每一个对象利用 toString 办法失去的所有字符串连接成一个字符串。

这意味着你把菜单中所有菜肴的名称连接起来,如下所示:

String shortMenu = menu.stream().map(Dish::getName).collect(joining());

留神:joining 在外部应用了 StringBuilder 来把生成的字符串一一追加起来。

此外 joining 工厂办法有一个重载版本能够承受元素之间的分界符,这样你就能够失去一个逗号分隔的菜肴名称列表:

String shortMenu = menu.stream().map(Dish::getName).collect(joining(","));

狭义的规约汇总

后面所提及的收集器都是一个能够用 reducing 工厂办法定义的规约过程的非凡状况而已。Collectors.reducing 工厂办法是所有这些非凡状况的一般化。

public static <T,U> Collector<T,?,U> reducing(U identity,
                    Function<? super T,? extends U> mapper, BinaryOperator<U> op)

参数解析:

第一个参数时规约操作的起始值,也是流中没有元素时的返回值。
第二个参数是 Function,将做肯定的转换操作。
第三个参数 BinaryOperator,将两个我的项目累积成一个同类型的值。
咱们将下面的示例转换一下:

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));
// 转换
Optional<Dish> mostCalorieDish =
        menu.stream().collect(reducing((d1,d2) -> d1.getCalories() > d1.getCalories() ? d1 : d2));

下面转换示例中,咱们应用的是一个单参数的 reducing 工厂办法创立的收集器,其能够看做是三个参数办法的非凡状况,它把流中的第一个我的项目作为终点,把恒等函数 (即一个函数仅仅是返回其输出参数) 作为一个转换函数。

2.2 分组
一个常见的数据库操作是依据一个或多个属性对汇合中的我的项目进行分组。

假如你要把菜单中的菜依照类型进行分类,有肉的放一组,有鱼的放一组,其余的都放另一组。用 Collectors.groupingBy 工厂办法返回的收集器就能够轻松地实现这项工作,如下所示:

Map<Dish.Type, List<Dish>> dishesByType =
        menu.stream().collect(groupingBy(Dish::getType));

这里,你给 groupingBy 办法传递了一个 Function(以办法援用的模式),它提取了流中每一道 Dish 的 Dish.Type。咱们把这个 Function 叫作分类函数,因为它用来把流中的元素分成不同的组。如下图所示,分组操作的后果是一个 Map,把分组函数返回的值作为映射的键,把流中所有具备这个分类值的我的项目的列表作为对应的映射值。在菜单分类的例子中,键就是菜的类型,值就是蕴含所有对应类型的菜肴的列表。

非凡利用示例:

public enum CaloricLevel{DIET,NORMAL,FAT}
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(groupingBy(dish -> {if (dish.getCalories() <= 400)
        return CaloricLevel.DIET;
    else if (dish.getCalories() <= 700)
        return CaloricLevel.NORMAL;
    else
        return CaloricLevel.FAT;
}));

以上为集体教训,心愿能给大家一个参考

正文完
 0