Java 8 - 收集器Collectors_归约和汇总

简介: Java 8 - 收集器Collectors_归约和汇总

20200510181139786.png


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


收集过程如下:

20210312011644415.png

在遍历流时,会把每一道菜都映射为其热量,然后把这个数字累加到一个累加器(这里的初始值 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;
    }


输出


20210312014217659.png

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


2021031201545946.png


好了 ,就到这儿吧

相关文章
|
6月前
|
Java
Java 8 Collectors 深入解析与示例
Java 8 Collectors 深入解析与示例
|
7月前
|
Java
Java8 Stream Collectors groupingBy使用
Java8 Stream Collectors groupingBy使用
Java流式操作——Collectors工具类
maxBy:获取流中最大元素;minBy:获取流中最小元素
|
Java
Java 8 - 收集器Collectors_实战
Java 8 - 收集器Collectors_实战
99 0
|
Java
Java 8 - 收集器Collectors_分区partitioningBy
Java 8 - 收集器Collectors_分区partitioningBy
125 0
|
安全 Java
Java 8 - 收集器Collectors_分组groupingBy
Java 8 - 收集器Collectors_分组groupingBy
312 0
|
Java 数据库
Java 8 - 收集器Collectors
Java 8 - 收集器Collectors
107 0
|
1天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
1天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
2天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
12 3