Java Stream中peek和map不为人知的秘密

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 本文通过一个Java Stream中的示例,探讨了`peek`方法在流式处理中的应用及其潜在问题。首先介绍了`peek`的基本定义与使用,并通过代码展示了其如何在流中对每个元素进行操作而不返回结果。接着讨论了`peek`作为中间操作的懒执行特性,强调了如果没有终端操作则不会执行的问题。文章指出,在某些情况下使用`peek`可能比`map`更简洁,但也需注意其懒执行带来的影响。

有段代码如下,这里我开始用Java Stream 中的map来修改对象的值

less

代码解读

复制代码

 retPage.setRecords(retList.stream().map(questionPageVO -> {
    questionPageVO.setCreateUserName(userIdAndUserMap.get(questionPageVO.getCreateId()).getUsername());
    questionPageVO.setUpdateUserName(userIdAndUserMap.get(questionPageVO.getUpdateId()).getUsername());
    return questionPageVO;
}).collect(Collectors.toList()));

但idea提示我这里可以替换为peek,

替换之后的写法

less

代码解读

复制代码

retPage.setRecords(retList.stream().peek(questionPageVO -> {
    questionPageVO.setCreateUserName(userIdAndUserMap.get(questionPageVO.getCreateId()).getUsername());
    questionPageVO.setUpdateUserName(userIdAndUserMap.get(questionPageVO.getUpdateId()).getUsername());
}).collect(Collectors.toList()));

这样确实更简单整洁了,但peek这样用真的合适吗? 今天我们就来讲一下peek的一些不为人知的缺点。

peek的基本定义和使用

  1. 先来看看peek的定义:

swift

代码解读

复制代码

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

peek方法接受一个Consumer参数,返回一个Stream结果。

Consumer是一个FunctionalInterface,它需要实现的方法是下面这个:

arduino

代码解读

复制代码

void accept(T t);

accept对传入的参数T进行处理,但是并不返回任何结果。

  1. peek的基本使用

csharp

代码解读

复制代码

public static void baseUse() {
    List<Integer> list = Stream.of(1,2,3)
            .peek(System.out::println)
            .collect(Collectors.toList());
    System.out.println(list);
}

输出内容:

csharp

代码解读

复制代码

1
2
3
[1, 2, 3]

3.  peek的流式处理

csharp

代码解读

复制代码

public static void peekForEach() {
    Stream.of(1,2,3)
            .peek(System.out::println)
            .forEach(e ->  System.out.println("forEach:" + e));
}

输出内容:

makefile

代码解读

复制代码

1
forEach:1
2
forEach:2
3
forEach:3

通过输出内容也可以看出,流式处理流程,是对应流中每一个元素,分别经历peekforEach操作(即一个元素执行完所有流程),而不是等peek完所有元素元素后再执行forEach

坑一:Stream的懒执行策略

之所以有流操作,是因为有时候处理的数据比较多,无法一次性加载到内存中。

为了优化stream的链式调用效率,stream还提供了一个懒加载策略。

什么是懒加载呢?

懒加载也叫intermediate operation, 在stream的方法中,大部分都是懒加载,另外部分则是terminal operation, 例如collectcount等,当有这种非懒加载的方法调用时,整个链式都会被执行,如开始的baseUse示例。

peekmap,都是懒加载方法,即intermediate operation

intermediate operation的特点是立即返回,如果最后没有以terminal operation结束,intermediate operation实际上是不会执行的。

贴个官方解释图

让我们来看这个示例:

csharp

代码解读

复制代码

public static void peekLazy() {
    Stream.of(1,2,3)
            .peek(e -> System.out.println("peek lazy: " + e));
}

执行之后,结果什么都没输出,表示peek中的逻辑没有被调用这里就是很大的一个坑,使用的时候要注意。

同理这里map也是一样。

csharp

代码解读

复制代码

public static void mapLazy() {
    Stream.of(1,2,3)
            .map(e -> {
                e = e+1;
                System.out.println("map lazy: " + e);
                return e;
            });
}


转载来源:https://juejin.cn/post/7394376050372657204

相关文章
|
19天前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
51 0
|
6天前
|
Java API C++
Java 8 Stream Api 中的 peek 操作
本文介绍了Java中`Stream`的`peek`操作,该操作通过`Consumer&lt;T&gt;`函数消费流中的每个元素,但不改变元素类型。文章详细解释了`Consumer&lt;T&gt;`接口及其使用场景,并通过示例代码展示了`peek`操作的应用。此外,还对比了`peek`与`map`的区别,帮助读者更好地理解这两种操作的不同用途。作者为码农小胖哥,原文发布于稀土掘金。
Java 8 Stream Api 中的 peek 操作
|
18天前
|
Java API
Java 8新特性:Lambda表达式与Stream API的深度解析
【7月更文挑战第61天】本文将深入探讨Java 8中的两个重要特性:Lambda表达式和Stream API。我们将首先介绍Lambda表达式的基本概念和语法,然后详细解析Stream API的使用和优势。最后,我们将通过实例代码演示如何结合使用Lambda表达式和Stream API,以提高Java编程的效率和可读性。
|
16天前
|
Java
盘点java8 stream中隐藏的函数式接口
`shigen`是一位坚持更新文章的博客作者,记录成长历程,分享认知见解,留住感动瞬间。本文介绍了函数式接口的概念及其在Java中的应用,包括`Comparator`、`Runnable`、`Callable`等常见接口,并详细讲解了`Function`、`Predicate`、`Consumer`、`Supplier`和`Comparator`等函数式接口的使用方法及应用场景,展示了如何利用这些接口简化代码并提高编程效率。**个人IP:shigen**,与shigen一起,每天进步一点点!
28 0
盘点java8 stream中隐藏的函数式接口
|
19天前
|
安全 Java API
Java 8 流库的魔法革命:Filter、Map、FlatMap 和 Optional 如何颠覆编程世界!
【8月更文挑战第29天】Java 8 的 Stream API 通过 Filter、Map、FlatMap 和 Optional 等操作,提供了高效、简洁的数据集合处理方式。Filter 用于筛选符合条件的元素;Map 对元素进行转换;FlatMap 将多个流扁平化合并;Optional 安全处理空值。这些操作结合使用,能够显著提升代码的可读性和简洁性,使数据处理更为高效和便捷。
28 0
|
21天前
|
存储 算法 Java
Stream很好,Map很酷,但答应我别滥用toMap()!
【8月更文挑战第27天】在Java的世界里,Stream API和Map数据结构无疑是现代编程中的两大瑰宝。Stream API以其函数式编程的优雅和强大的数据处理能力,让集合操作变得简洁而高效;而Map则以其键值对的存储方式,为数据的快速检索和更新提供了便利。然而,当这两者相遇,特别是当我们试图通过Stream的toMap()方法将流中的元素转换为Map时,一些潜在的问题和陷阱便悄然浮现。今天,我们就来深入探讨一下这个话题,并探讨如何更加安全、高效地利用这些强大的工具。
26 0
|
21天前
|
Java API 网络安全
探索Java中的Stream API:从基础到高级应用云计算与网络安全:技术融合与挑战
【8月更文挑战第27天】在Java的海洋中,Stream API犹如一艘强大的船,让开发者能以声明式的方式处理集合数据。本文将启航,先带你了解Stream的基本概念和用法,再深入探讨其高级特性,如并行流、管道操作以及性能考量。我们将通过具体代码示例,展示如何高效利用Stream API简化数据处理流程,提升代码的可读性和性能。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往更优雅编程风格的大门。
|
27天前
|
安全 Java
【Java集合类面试三】、Map接口有哪些实现类?
这篇文章介绍了Java中Map接口的几种常用实现类:HashMap、LinkedHashMap、TreeMap和ConcurrentHashMap,以及它们适用的不同场景和线程安全性。
|
3月前
|
存储 算法 Java
滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析
【6月更文挑战第19天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
32 3
滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析
|
2月前
|
Java
Java Map.Entry接口详解
Java Map.Entry接口详解