Bootstrap

6.3 分组


根据菜的类型给菜分组 :

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(以方法引用的形式),它提取了流中每一道DishDish.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接受两个参数, 要转换的收集器及转换函数,并返回另一个收集器。
具体的工作流程
![[Pasted image 20241202163035.png]]

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());  
    }  
}
;