Stream 强大,Map 精妙,但千万别碰 toMap()!

科技   2024-07-31 12:08   四川  
嗨,我是东哥。

今天要跟大家聊聊 Java 8 中的 Stream 流处理。作为一名老程序员,Stream 是真的美妙,但有一个地方你得注意,那就是别用 toMap() 方法。不信?那让我们一起来看看这个折腾人的故事。

# Java 8 引入的 Stream


自从 Java 8 引入了 Stream 这个功能,代码简洁程度飞跃了好几个台阶。Stream 流式操作简直是救了我这条懒得动的老命,从此我可以优雅地进行各种数据处理,尤其是在集合数据的处理上,真是事半功倍。

平时我们用 Stream 最常见的操作无非是 collect(Collectors.toList()) 和 collect(Collectors.toSet()),这让我们在处理集合数据时非常方便快捷。但是,当你尝试使用 toMap() 时,事情就没那么简单了。

别用 toMap() 的原因


你可能会想,既然 toList 和 toSet 都这么好用,toMap() 也应该差不多吧?事实是,一旦你用错了,麻烦就来了。

首先,让我们看一个简单的示例。我们有一个用户实体类,结构如下:
@Data@AllArgsConstructor  public class User {      private int id;      private String name;  }

假设我们从数据库中读取了一个用户列表,并希望将其转换为 Map,其中 key 是用户的 id,value 是用户的 name。你可能会很自然地写出以下代码:
public class UserTest {    @Test    public void demo() {          List<User> userList = new ArrayList<>();        // 模拟数据        userList.add(new User(1, "Alex"));          userList.add(new User(1, "Beth"));
Map<Integer, String> map = userList.stream() .collect(Collectors.toMap(User::getId, User::getName)); System.out.println(map); }}

运行程序后,你期待看到一切正常,结果却收到一个 IllegalStateException 错误,提示 Key 值重复。

Key 重复的处理


作为资深程序员,第一反应是检查 HashMap 的机制,Key 重复应该进行替换才对啊。于是,我们尝试修改代码来手动处理重复 Key 的情况:
public class UserTest {    @Test    public void demo() {          List<User> userList = new ArrayList<>();        // 模拟数据        userList.add(new User(1, "Alex"));          userList.add(new User(1, "Beth"));
Map<Integer, String> map = userList.stream() .collect(Collectors.toMap(User::getId, User::getName, (oldData, newData) -> newData)); System.out.println(map); }}

然而,再次运行时,你发现又出现了新的问题——NullPointerException (NPE)。这时候你会发现原来 User::getName 可能会返回 null 值。

使用 Optional 避免 NPE


为了避免 NPE,我们可以使用 Optional 来包装 null 值:
public class UserTest {    @Test    public void demo() {          List<User> userList = new ArrayList<>();        // 模拟数据        userList.add(new User(1, "Alex"));          userList.add(new User(1, "Beth"));        userList.add(new User(2, null));
Map<Integer, String> map = userList.stream() .collect(Collectors.toMap( User::getId, user -> Optional.ofNullable(user.getName()).orElse(""), (oldData, newData) -> newData) ); System.out.println(map); }}

这段代码看起来是优雅了不少,但你真的需要这么复杂的处理吗?用 Stream 和 Optional 确实能解决问题,但也可能让代码的可读性变得差。

回归传统 for 循环


最后,我们还是得承认,有时候传统的 for 循环更加简洁明了:
public class UserTest {    @Test    public void demo() {          List<User> userList = new ArrayList<>();        // 模拟数据        userList.add(new User(1, "Alex"));          userList.add(new User(1, "Beth"));        userList.add(new User(2, null));
Map<Integer, String> map = new HashMap<>(); userList.forEach(user -> { map.put(user.getId(), user.getName() != null ? user.getName() : ""); }); System.out.println(map); }}

这段代码不仅易于理解,而且避免了很多潜在的问题。

# 结论


虽然 Stream 的 toMap() 方法提供了一种简洁的方式将流转换为 Map,但在处理重复 Key 和 null 值时需要特别小心。某些情况下,回归传统的 for 循环可能会让代码更加清晰明了。

所以,东哥劝你一句,尽量避免使用 toMap(),在需要的时候多考虑一下传统的方式。祝大家编码愉快,别被 Stream 玩坏了!💻👨‍💻

怎么样,各位大佬,你们在开发中有没有遇到过类似的问题?欢迎在评论区分享你们的经验和见解!
对编程、职场感兴趣的同学,可以链接我,微信:coder301 拉你进入“程序员交流群”。


🔥东哥私藏精品 热门推荐🔥

东哥作为一名超级老码农,整理了全网最全《Java高级架构师资料合集》

资料包含了《IDEA视频教程》《最全Java面试题库》、最全项目实战源码及视频》及《毕业设计系统源码》总量高达 650GB 。全部免费领取!全面满足各个阶段程序员的学习需求。

Java面试那些事儿
回复 java ,领取Java面试题。分享AI编程,Java教程,Java面试辅导,Java编程视频,Java下载,Java技术栈,AI工具,Java开源项目,Java简历模板,Java招聘,Java实战,Java面试经验,IDEA教程。
 最新文章