Java8流的Collectors知识点补充

Java8流的Collectors知识点补充Java8新特性Collectors_collectors.reducing

0.绪论

我们可以将流看做花哨又懒惰的数据集迭代器,它们支持两种类型的操作:
1.中间操作如(filter、map)
2.终端操作如(count、findFirst、foreach和reduce)
中间操作可以链接起来讲一个流转化为另一个流,这些操作并不会消耗流,其目的就是建立一个流水线,而终端操作会消耗流以产生一个最终的结果,例如返回流中的一个最大元素。

1.filter筛选

Streams接口支持filter方法,该方法支持一个谓词作为参数,并返回一个包括所有符合谓词的元素的流。

List<Dish> vagetarianMenu = menu
								.stream()
								.filter(Dish::isVegetarian)
								.collect(Collectors.toList);

具体的效果见下图
在这里插入图片描述


2.Collectors reducing收集器

 import java.util.stream.Collectors;
 Employee employee = new Employee(9000, "man", 19, "888888888");
 Employee employee1 = new Employee(8000, "sex", 21, "77777777777777777");
 Employee employee2 = new Employee(29000, "man", 22, "888888888888888777");

 int salarySum = Arrays.asList(employee, employee1, employee2)
                .stream()
                .collect(Collectors.reducing(0,
                        Employee::getSalary, Integer::sum));
 System.out.println(salarySum); //46000

3.Collectors groupingBy多级分组

  List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));

Map<Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel = menu
                .stream()
                .collect(
                        Collectors.groupingBy(Dish::getType,
                                Collectors.groupingBy(dish -> { 
   
                                    if (dish.getCalories() <= 400) { 
   
                                        return CaloricLevel.DIET;
                                    } else if (dish.getCalories() <= 700) { 
   
                                        return CaloricLevel.NORMAL;
                                    } else { 
   
                                        return CaloricLevel.FAT;
                                    }
                                })));
System.out.println(dishesByTypeCaloricLevel);
/** { OTHER={NORMAL=[french fries, pizza], DIET=[rice, season fruit]}, FISH={NORMAL=[salmon], DIET=[prawns]}, MEAT={FAT=[pork], NORMAL=[beef], DIET=[chicken]} } */
public enum CaloricLevel { 
   
    DIET, NORMAL, FAT
}
public class Dish { 
   
    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    public enum Type { 
   MEAT, FISH, OTHER}

    public Dish(String name, boolean vegetarian, int calories, Type type) { 
   
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() { 
   
        return name;
    }

    public boolean isVegetarian() { 
   
        return vegetarian;
    }

    public int getCalories() { 
   
        return calories;
    }

    public Type getType() { 
   
        return type;
    }

    @Override
    public String toString() { 
   
        return name;
    }
}

在这里插入图片描述

4.Collectors 按子组收集数据

 List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));

Map<Dish.Type,Long>  typesCount= menu
									.stream()
									.collect(Collectors.groupingBy(Dish::getType,Collectors.counting()));
System.out.println(typesCount); // {MEAT=3, FISH=2, OTHER=4}

5.把收集器的结果转换为另外一种类型

首先举一个例子:分类后拿到热量最高的菜

List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));

Map<Dish.Type, Optional<Dish>> mostCaloricByType = menu
                .stream()
                .collect(Collectors.groupingBy(Dish::getType,
                                Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))));
System.out.println(mostCaloricByType);
// result:{OTHER=Optional[pizza], FISH=Optional[salmon], MEAT=Optional[pork]}

但是这个Map中的value是Optional,我们想直接拿到Dish,这时候我们可以使用collectingAndThen,如下

List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));
 Map<Type, Dish> mostCaloricByType = menu
                .stream()
                .collect(Collectors.groupingBy(
                        Dish::getType,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)),
                                Optional::get)));
System.out.println(mostCaloricByType);

这个工厂方法接受两个参数——要转换的收集器以及转换函数,并返回另一个收集器。这个收集器相当于旧收集器的一个包装, collect操作的最后一步就是将返回值用转换函数做一个映射。在这里,被包起来的收集器就是用maxBy建立的那个,而转换函数Optional::get则把返回的Optional中的值提取出来。前面已经说过,这个操作放在这里是安全的,因为reducing收集器永远都不会返回Optional.empty()。其结果是下面的Map:
{FISH=salmon, OTHER=pizza, MEAT=pork}


6.与groupingBy联合使用的其他收集器的例子

例子1:

 List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));

 Map<Dish.Type, Integer> totalCaloriesByType =
                menu
                    .stream()
                    .collect(Collectors.groupingBy(Dish::getType,
                                Collectors.summingInt(Dish::getCalories)));
System.out.println(totalCaloriesByType);
// {MEAT=1900, OTHER=1550, FISH=750}

例子2:

List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));

        Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
                menu
                        .stream()
                        .collect(Collectors.groupingBy(Dish::getType, Collectors.mapping(
                                dish -> { 
   
                                    if (dish.getCalories() <= 400) { 
   
                                        return CaloricLevel.DIET;
                                    } else if (dish.getCalories() <= 700) { 
   
                                        return CaloricLevel.NORMAL;
                                    } else { 
   
                                        return CaloricLevel.FAT;
                                    }
                                },
                                Collectors.toSet())));
System.out.println(caloricLevelsByType);
// {MEAT=[NORMAL, FAT, DIET], OTHER=[NORMAL, DIET], FISH=[NORMAL, DIET]}

也可以这样:

 Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
                menu
                        .stream()
                        .collect(Collectors.groupingBy(Dish::getType, Collectors.mapping(
                                dish -> { 
   
                                    if (dish.getCalories() <= 400) { 
   
                                        return CaloricLevel.DIET;
                                    } else if (dish.getCalories() <= 700) { 
   
                                        return CaloricLevel.NORMAL;
                                    } else { 
   
                                        return CaloricLevel.FAT;
                                    }
                                },
                                Collectors.toCollection(HashSet::new))));
System.out.println(caloricLevelsByType);

7.分区

分区是分组的特殊情况:由一个谓词(返回一个布尔值的函数)作为分类函数,它称分区函数。分区函数返回一个布尔值,这意味着得到的分组Map的键类型是Boolean,于是它最多可以分为两组——true是一组, false是一组

  List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));
  Map<Boolean, List<Dish>> partitionedMenu =
                menu
                    .stream()
                    .collect(Collectors.partitioningBy(Dish::isVegetarian));
 System.out.println(partitionedMenu);
 // {false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}

细分(素食菜肴(按类型)):

 Map<Boolean, Map<Type, List<Dish>>> vegetarianDishesByType =
                menu
                    .stream()
                    .collect(Collectors.partitioningBy(Dish::isVegetarian,
                            Collectors.groupingBy(Dish::getType)));
        System.out.println(vegetarianDishesByType);
// {false={MEAT=[pork, beef, chicken], FISH=[prawns, salmon]}, true={OTHER=[french fries, rice, season fruit, pizza]}} 

附录

1.Employee类

public class Employee { 
   

    private Integer salary;
    private String sex;
    private Integer age;
    private String phone;

    public Employee(Integer salary, String sex, Integer age, String phone) { 
   
        this.salary = salary;
        this.sex = sex;
        this.age = age;
        this.phone = phone;
    }

    public Integer getSalary() { 
   
        return salary;
    }

    public void setSalary(Integer salary) { 
   
        this.salary = salary;
    }

    public String getSex() { 
   
        return sex;
    }

    public void setSex(String sex) { 
   
        this.sex = sex;
    }

    public Integer getAge() { 
   
        return age;
    }

    public void setAge(Integer age) { 
   
        this.age = age;
    }

    public String getPhone() { 
   
        return phone;
    }

    public void setPhone(String phone) { 
   
        this.phone = phone;
    }

    @Override
    public String toString() { 
   
        return "Employee{" +
                "salary=" + salary +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

2.其他人写的很不错关于流操作的文章
3.Collectors类的静态工厂方法
在这里插入图片描述
在这里插入图片描述

今天的文章Java8流的Collectors知识点补充分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88597.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注