嗨,我是东哥。
今天要跟大家聊聊 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 玩坏了!
怎么样,各位大佬,你们在开发中有没有遇到过类似的问题?欢迎在评论区分享你们的经验和见解!
领取专属 10元无门槛券
私享最新 技术干货