根据菜的类型给菜分组 :
List<Dish> menu = Dish.menu;
Map<Dish.Type, List<Dish>> collect = menu.stream().collect(Collectors.groupingBy(Dish::getType));
System.out.println(collect);
输出 :
{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza], MEAT=[pork, beef, chicken]}
这里给Collectors.groupingBy
方法传递了一个Function(以方法引用的形式)
,它提取了流中每一道Dish
的Dish.Type
。这个Function
叫作分类函数
,它用来把流中的元素分成不同的组。
分组操作的结果是一个Map
,把分组函数
返回的值作为映射的键,把流中所有具有这个分类值的项目的列表作为对应的映射值。
按热量为菜分组
enum CaloricLevel { DIET, NORMAL, FAT };
Map<Grouping.CaloricLevel, List<Dish>> map = menu.stream().collect(Collectors.groupingBy(dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}));
输出 :
{FAT=[pork, beef], DIET=[rice, season fruit], NORMAL=[chicken, french fries, pizza, prawns, salmon]}
6.3.1 多级分组
要实现多级分组,我们可以使用一个由双参数版本的Collectors.groupingBy
工厂方法创建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。
例如先按照类别分组,再按照热量进行分组
Map<Dish.Type, Map<Grouping.CaloricLevel, List<Dish>>> mapMap = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.groupingBy(
dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}
)));
System.out.println(mapMap);
输出 :
{FISH={NORMAL=[prawns, salmon]}, OTHER={DIET=[rice, season fruit], NORMAL=[french fries, pizza]}, MEAT={FAT=[pork, beef], NORMAL=[chicken]}}
6.3.2 按子组收集数据
Collectors.groupingBy
的第二个参数不一定非得是Collectors.groupingBy
,你还可以借此来收集一个分组的信息
// 收集每种菜的个数
System.out.println(menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting())));
// 收集每种菜的总热量
System.out.println(menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.summingInt(Dish::getCalories))));
// 收集每种菜中热量最大的菜
Map<Dish.Type, Optional<Dish>> optionalMap = menu.stream().
collect(Collectors.groupingBy(Dish::getType,
Collectors.maxBy(Comparator.comparing(Dish::getCalories))));
System.out.println(optionalMap);
输出 :
{FISH=2, OTHER=4, MEAT=3}
{FISH=850, OTHER=1550, MEAT=1900}
{FISH=Optional[salmon], OTHER=Optional[pizza], MEAT=Optional[pork]}
去掉Optional
的方法。
Map<Dish.Type, Dish> collected = menu.stream().collect(Collectors.groupingBy(Dish::getType,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Dish::getCalories)), Optional::get)));
System.out.println(collected);
输出 :
{FISH=salmon, OTHER=pizza, MEAT=pork}
Collectors.collectingAndThen
接受两个参数, 要转换的收集器及转换函数,并返回另一个收集器。
具体的工作流程
Map<Dish.Type, Set<Grouping.CaloricLevel>> setMap = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.mapping(
dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}, Collectors.toSet())));
System.out.println(setMap);
输出 :
{FISH=[NORMAL], OTHER=[DIET, NORMAL], MEAT=[FAT, NORMAL]}
Collectors.mapping
收集器接收两个参数:一个函数对流中的元素做变换,另一个则将变换的结果对象收集起来(又是一个收集器)
孩子,喜欢Java8的收集器吗?
对于Collectors.toSet
的具体类型也是可以控制的,可以使用Collectors.toCollection
方法,可以传递一个容器的构造器给这个方法。
Map<Dish.Type, Set<Grouping.CaloricLevel>> hashMap = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.mapping(
dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}, Collectors.toCollection(HashSet::new))));
System.out.println(hashMap);
System.out.println(hashMap.getClass().getSimpleName());
输出 :
{FISH=[NORMAL], OTHER=[DIET, NORMAL], MEAT=[FAT, NORMAL]}
HashMap
下面是完整的程序
package lambdasinaction.chap6;
import java.util.*;
import java.util.stream.Collectors;
public class Test03 {
public static void main(String[] args) {
List<Dish> menu = Dish.menu;
Map<Dish.Type, List<Dish>> collect = menu.stream().collect(Collectors.groupingBy(Dish::getType));
System.out.println(collect);
System.out.println("============================");
Map<Grouping.CaloricLevel, List<Dish>> map = menu.stream().collect(Collectors.groupingBy(dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}));
System.out.println(map);
System.out.println("============================");
Map<Dish.Type, Map<Grouping.CaloricLevel, List<Dish>>> mapMap = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.groupingBy(
dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}
)));
System.out.println(mapMap);
System.out.println("============================");
System.out.println(menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting())));
System.out.println(menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.summingInt(Dish::getCalories))));
Map<Dish.Type, Optional<Dish>> optionalMap = menu.stream().
collect(Collectors.groupingBy(Dish::getType,
Collectors.maxBy(Comparator.comparing(Dish::getCalories))));
System.out.println(optionalMap);
System.out.println("============================");
Map<Dish.Type, Dish> collected = menu.stream().collect(Collectors.groupingBy(Dish::getType,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Dish::getCalories)), Optional::get)));
System.out.println(collected);
System.out.println("============================");
Map<Dish.Type, Set<Grouping.CaloricLevel>> setMap = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.mapping(
dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}, Collectors.toSet())));
System.out.println(setMap);
System.out.println("============================");
Map<Dish.Type, Set<Grouping.CaloricLevel>> hashMap = menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.mapping(
dish -> {
if (dish.getCalories() < 400) return Grouping.CaloricLevel.DIET;
else if (dish.getCalories() < 700) return Grouping.CaloricLevel.NORMAL;
return Grouping.CaloricLevel.FAT;
}, Collectors.toCollection(HashSet::new))));
System.out.println(hashMap);
System.out.println(hashMap.getClass().getSimpleName());
}
}