文章目录
Pre
在需要将流项目重组成集合时,一般会使用收集器( Stream 方法 collect的参数)。再宽泛一点来说,但凡要把流中所有的项目合并成一个结果时就可以用。这个结果可以是任何类型。
public static List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH));
来看个简单的需求: 利用 counting 工厂方法返回的收集器,统计菜单中有多少种菜
public static Long howManyDishes(List<Dish> menu) {
// return menu.stream().count();
return menu.stream().collect(Collectors.counting());
}
还可以写得更为直接:
return menu.stream().count();
查找流中的最大值和最小值
需求:想要找出热量最高的菜和热量最低的菜
public static Optional<Dish> highCa(List<Dish> menu) {
Optional<Dish> collect = menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)));
return collect;
}
public static Optional<Dish> lowC(List<Dish> menu) {
Optional<Dish> collect = menu.stream().collect(Collectors.minBy(Comparator.comparing(Dish::getCalories)));
return collect;
}
可以使用两个收集器, Collectors.maxBy 和Collectors.minBy ,来计算流中的最大或最小值。这两个收集器接收一个 Comparator 参数来比较流中的元素。
可以创建一个 Comparator 来根据所含热量对菜肴进行比较,并把它传递给Collectors.maxBy :
Comparator<Dish> dishCaloriesComparator =
Comparator.comparingInt(Dish::getCalories);
然后进行计算
Optional<Dish> collect = menu.stream().collect(Collectors.maxBy(dishCaloriesComparator ));
Optional<Dish>
是怎么回事。要回答这个问题,我们需要问“要是 menu 为空怎么办”。那就没有要返回的?了!Java 8引入了 Optional ,它是一个容器,可以包含也可以不包含值。这里它完美地代表了可能也可能不返回菜肴的情况。
汇总
另一个常见的返回单个值的归约操作是对流中对象的一个数值字段求和、求平均数等等。这种操作被称为汇总操作。让我们来看看如何使用收集器来表达汇总操作。
Collectors 类专门为汇总提供了一个工厂方法: Collectors.summingInt 。它可接受一 个把对象映射为求和所需 int 的函数,并返回一个收集器;该收集器在传递给普通的 collect 方法后即执行我们需要的汇总操作。
需求: 求出菜单列表的总热量
public static Integer allCal(List<Dish> menu) {
Integer collect = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
return collect;
}
收集过程如下:
在遍历流时,会把每一道菜都映射为其热量,然后把这个数字累加到一个累加器(这里的初始值 0 )。
Collectors.summingLong
和 Collectors.summingDouble
方法的作用完全一样,可以用于求和字段为 long 或 double 的情况。
但汇总不仅仅是求和;还有 Collectors.averagingInt
,连同对应的 averagingLong
和averagingDouble
可以计算数值的平均数:
public static Double avg(List<Dish> menu) {
Double collect = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));
return collect;
}
截止到现在,我们使用收集器来给流中的元素计数,找到这些元素数值属性的最大值和最小值,以及计算其总和和平均值。
需求: 一次操作求出菜单中元素的个数,并得总和、平均值、最大值和最小值 (summarizingXXX)
public static IntSummaryStatistics sumInfo(List<Dish> menu) {
IntSummaryStatistics collect = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
return collect;
}
输出
summarizingInt
这个收集器会把所有这些信息收集到一个叫作 IntSummaryStatistics
的类里,它提供了方便的取值(getter)方法来访问结果。
同样,相应的 summarizingLong
和 summarizingDouble
工厂方法有相关的 LongSummary-Statistics
和 DoubleSummaryStatistics
类型,适用于收集的属性是原始类型 long 或double 的情况。
连接字符串
joining 工厂方法返回的收集器会把对流中每一个对象应用 toString 方法得到的所有字符串连接成一个字符串。
需求 :把菜单中所有菜肴的名称连接起
public static String joinMenu(List<Dish> menu) {
return menu.stream().map(Dish::getName).collect(Collectors.joining());
}
请注意, joining 在内部使用了 StringBuilder 来把生成的字符串逐个追加起来。
此外还要注意,如果 Dish 类有一个 toString 方法来返回菜肴的名称,那你无需用提取每一道菜名称的函数来对原流做映射就能够得到相同的结果。
String shortMenu = menu.stream().collect(joining());
但该字符串的可读性并不好。幸好, joining 工厂方法有一个重载版本可以接受元素之间的分界符,这样你就可以得到一个指定分隔符的名称列表:
public static String joinMenu(List<Dish> menu) {
return menu.stream().map(Dish::getName).collect(joining(","));
}
好了 ,就到这儿吧