Java8中Stream API的peek()方法详解及需要注意的坑

 更新时间:2024年06月06日 16:21:01   作者:不脱顶的程序员小王  
这篇文章主要给大家介绍了关于Java8中Stream API的peek()方法详解及需要注意的坑,Java 中的 peek 方法是 Java 8 中的 Stream API 中的一个方法,它属于中间操作,文中通过代码介绍的非常详细,需要的朋友可以参考下

引言

在Java 8中,Stream API引入了许多强大的函数式编程特性,极大地增强了我们对集合数据进行操作的能力。其中一个很有用的方法就是peek(),本文将详细介绍其功能及应用场景。

peek() 方法简介

peek() 是Java 8 Stream API中的一个中间操作方法,它的主要功能是对流中的每个元素执行一个操作(可以是获取、修改或打印等),而不影响流的整体处理流程。这意味着即使使用了peek(),流也可以继续进行后续的映射、过滤或其他操作。

<T> Stream<T> peek(Consumer<? super T> action);

参数action是一个Consumer接口的实现,它接受一个泛型参数T,并对其执行某种操作。

示例一:简单使用 peek() 打印元素()

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class PeekExample {
    public static void main(String[] args) {
        List<String> addrList = Arrays.asList("AAA");

        addrList.stream()
                .filter(Objects::nonNull)
                .peek(info -> {
                    System.out.println("Processing Element: " + info);
                }).collect(Collectors.toList());

        // 输出:Processing Element: AAA
    }
}

在这个例子中,我们首先创建了一个包含一个元素"AAA"的列表,并将其转换为流。随后使用filter()方法去除可能存在的空元素(在此例中其实无需过滤,因为已知元素不为空)。关键在于peek()方法的应用,它接收一个lambda表达式,每当流中的元素被访问时,就执行该表达式,从而实现了打印当前处理元素的功能。

有坑的点:删除流终止操作,将会不执行。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class PeekExample {
    public static void main(String[] args) {
        List<String> addrList = Arrays.asList("AAA");

        addrList.stream()
                .filter(Objects::nonNull)
                .peek(info -> {
                    System.out.println("Processing Element: " + info);
                });
        // 输出:
    }
}

这段代码中peek不会被执行,并且不会打印

peek()方法是 Strean 接口中的一个中间操作,它允许你在流的每 个元素上执行一个操作,但是这个操作是在最终的终端操作(如
forEach,collect,limit 等)执行 前进行的。 然而,如果 peek()是流中唯一的操作,那么它实际上不会执行。这是因为
peek ()本身并不是一个 终端操作,它不会触发流的执行。在 jav 8 及以后的版本中,流的执行是情性的,这意味着流操作
不会立即执行,而是在遇到终端操作时才会实际执行

示例二:结合 peek() 进行调试

peek()方法的一个常见用途是在调试时查看流中的元素状态,而不会影响到流的最终处理结果。

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
numbers.map(n -> n * 2) // 将每个数乘以2
       .peek(n -> System.out.println("Mapped value: " + n))
       .filter(n -> n % 3 == 0) // 过滤出能被3整除的数
       .peek(n -> System.out.println("Filtered value: " + n))
       .collect(Collectors.toList()); // 收集到List中

在上述代码中,我们在映射和过滤操作之间插入了两次peek(),分别用来查看映射后的值和过滤后的值,这对于理解流的处理过程非常有帮助。

总结起来,peek()方法就像是一个观察者,可以在不影响流整体处理的情况下,让我们有机会在每个元素上执行一些额外的操作,例如日志记录、临时计算、调试信息打印等。但它并不改变流的原始内容,也不决定流的最终输出结果。要得到流的处理结果,还需要进一步调用诸如collect()forEach()reduce()等终端操作方法。

需要注意的坑

坑一:peek() 不影响流的生成和消费

peek()是一个中间操作,它并不会终止流的处理流程,因此如果不跟一个终端操作(如collect(), forEach(), count()等),则peek()中的操作虽然会被执行,但整个流式处理链的结果不会有任何产出。换言之,只有当流被消耗时,peek()里的操作才会真正发生。

坑二:peek() 的执行次数取决于下游操作

peek()方法中的动作会在流的每个元素上执行一次,但具体执行多少次取决于下游的终端操作。例如,如果你在排序(sorted())前使用了peek(),而在排序后又使用了一次peek(),则同一个元素可能会被两次peek()。

坑三:并发流中的peek()行为

对于并行流,peek()操作的执行顺序没有保证,而且可能会多次执行(取决于JVM的具体调度)。如果你在并行流中依赖peek()的顺序性或唯一性,可能会遇到意想不到的问题。

坑四:资源管理

如果在peek()中打开了一些资源(如文件、数据库连接等),但在peek()内部并未妥善关闭它们,可能会导致资源泄露。因为在没有终端操作的情况下,流可能不会立即执行,资源也就无法及时释放。

坑五:对流元素的修改可能无效

peek()通常用于读取或打印流元素,而不是修改它们。虽然理论上可以尝试在peek()中修改元素,但由于流的惰性求值和可能的不可变性,这样的修改可能不会反映到源集合或后续流操作中。

总结

到此这篇关于Java8中Stream API的peek()方法详解及需要注意的坑的文章就介绍到这了,更多相关Java8 Stream API的peek()方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot整合JWT的实现步骤

    Spring Boot整合JWT的实现步骤

    本文主要介绍了Spring Boot整合JWT,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java 面试题和答案 - (下)

    Java 面试题和答案 - (下)

    本文主要介绍Java 面试题,这里整理了Java面试题关于JDBC,线程异常处理,Servlet,JSP的知识的整理,帮助大家理解知识点,便于面试,有兴趣的小伙伴可以参考下
    2016-09-09
  • Java如何通过jstack命令查询日志

    Java如何通过jstack命令查询日志

    在分析线上问题时常使用到jstack <PID>命令将当时Java应用程序的线程堆栈dump出来,面对jstack 日志,我们如何查看?下面小编给大家介绍下Java如何通过jstack命令查询日志,感兴趣的朋友一起看看吧
    2023-03-03
  • spring boot自动装配之@ComponentScan注解用法详解

    spring boot自动装配之@ComponentScan注解用法详解

    @ComponentScan的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中,下面这篇文章主要给大家介绍了关于spring boot自动装配之@ComponentScan注解用法的相关资料,需要的朋友可以参考下
    2023-04-04
  • Java中的Enum枚举使用方法解析

    Java中的Enum枚举使用方法解析

    这篇文章主要介绍了Java中的Enum枚举使用方法解析,枚举是一种数据类型,和int、double、string等类型相似,枚举用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型,需要的朋友可以参考下
    2023-11-11
  • Spring细数两种代理模式之静态代理和动态代理概念及使用

    Spring细数两种代理模式之静态代理和动态代理概念及使用

    代理是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。可以不修改目标对象,对目标对象功能进行拓展。在我们学习Spring的时候就会发现,AOP(面向切面编程)的底层就是代理
    2023-02-02
  • Spring Bean初始化及销毁多种实现方式

    Spring Bean初始化及销毁多种实现方式

    这篇文章主要介绍了Spring Bean初始化及销毁多种实现方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • java自动根据文件内容的编码来读取避免乱码

    java自动根据文件内容的编码来读取避免乱码

    这篇文章主要介绍了java自动根据文件内容的编码来读取避免乱码,需要的朋友可以参考下
    2014-02-02
  • SpringCloud openfeign相互调用实现方法介绍

    SpringCloud openfeign相互调用实现方法介绍

    在springcloud中,openfeign是取代了feign作为负载均衡组件的,feign最早是netflix提供的,他是一个轻量级的支持RESTful的http服务调用框架,内置了ribbon,而ribbon可以提供负载均衡机制,因此feign可以作为一个负载均衡的远程服务调用框架使用
    2022-11-11
  • Spring解决循环依赖的方法(三级缓存)

    Spring解决循环依赖的方法(三级缓存)

    今天,我们要说的是spring是如何解决循环依赖的。对于一个问题说解决之前,我们首先要先明确形成问题的本因。那么循环依赖,何为循环依赖呢?感兴趣的朋友跟随小编一起看看吧
    2021-11-11

最新评论