《Effective Java》解读第45条:谨慎使用Stream

第45条:谨慎使用Stream

Stream API功能强大,但过度使用会严重损害代码的可读性和可维护性。明智的做法是找到Stream与循环之间的平衡点,在合适的场景使用合适的工具。

什么时候适合使用Stream?

统一转换元素序列:批量对集合元素做相同变换(如map操作)

过滤元素序列:按条件筛选元素(如filter操作)

组合元素序列:求和、连接、求最小值等聚合操作

累积到集合:将元素收集到集合中,或按属性分组

搜索元素:查找满足条件的元素

书中"换位词"的例子:

找出字母相同、顺序不同的单词,并打印出单词数大于最低要求的单词。

恰到好处地使用Stream让代码更短更清晰:

java 复制代码
// 明智地使用Stream
try (Stream<String> words = Files.lines(dictionary)) {
    words.collect(groupingBy(word -> alphabetize(word)))
         .values().stream()
         .filter(group -> group.size() >= minGroupSize)
         .forEach(g -> System.out.println(g.size() + ": " + g));
}

alphabetize方法是获取单词字母重排后的字符串。

什么时候应该避免Stream?

滥用Stream会使程序代码更加难以读懂和维护。

以下是一些需要尽量避免使用Stream的情况:

  1. 需要读取或修改局部变量(Lambda只能读取final或effectively final的变量,无法修改任何局部变量)
  2. 需要从封闭方法中返回、break或continue(Lambda无法做到这些控制流操作)
  3. 需要抛出受检异常(Lambda无法抛出受检异常)
  4. 过度使用流的典型反例

还是上一个例子:

java 复制代码
// 过度使用Stream
try (Stream<String> words = Files.lines(dictionary)) {
    words.collect(groupingBy(word -> word.chars().sorted()
              .collect(StringBuilder::new,
                       (sb, c) -> sb.append((char) c),
                       StringBuilder::append).toString()))
         .values().stream()
         .filter(group -> group.size() >= minGroupSize)
         .map(group -> group.size() + ": " + group)
         .forEach(System.out::println);
}

注意:

chars()使用Stream会返回IntStream,如果需要char,需要进行类型转换。

java 复制代码
// 错误示例
"hello world".chars().forEach(System.out::print);
System.out.println();
// 正确示例
"hello world".chars().forEach( t -> System.out.print((char)t));

实用建议

  1. 辅助方法提升可读性
java 复制代码
// 将字母排序逻辑提取为方法
words.collect(groupingBy(word -> alphabetize(word)))
  1. 谨慎使用并行流:并行流虽然看似简单,但很少能真正提升性能,反而可能引入并发问题。
  2. 保持平衡、恰当使用:仅在有意义的情况下在新代码中使用它们,Stream本身是简化代码,而不是用来炫技。
相关推荐
Mahir08几秒前
Spring 核心原理:IoC/DI 与 Bean 生命周期全景解析
java·后端·spring·面试·bean生命周期·控制反转ioc·依赖注入di
weixin_489690021 分钟前
NAS部署实测:Solon vs Spring Boot,从内存到包体积的“降维打击”
java·spring boot·后端
我命由我123454 分钟前
Android 开发问题:TextView 内容超过宽度时,默认不会换行
android·开发语言·java-ee·android studio·android jetpack·android-studio·android runtime
计算机安禾4 分钟前
【c++面向对象编程】第36篇:析构函数应永远不抛出异常——原因与最佳实践
开发语言·c++
tongluowan0078 分钟前
怎么保证缓存和数据库的一致性
java·数据库·缓存·一致性
一条泥憨鱼8 分钟前
【Java 进阶】LinkedHashMap 与 TreeMap
java·开发语言·数据结构·笔记·后端·学习
ゆづき8 分钟前
假如编程语言们有外号
java·c语言·c++·python·学习·c#·生活
凤山老林10 分钟前
63-Java LinkedList(链表)
java·开发语言·链表
恣艺16 分钟前
用Go从零实现一个高性能KV存储引擎:B+Tree索引、WAL持久化、LRU缓存的工程实践
开发语言·数据库·redis·缓存·golang
TDengine (老段)16 分钟前
TDengine 支持数据类型深度解析 — 类型体系、存储编码与选型指南
java·大数据·数据库·系统架构·时序数据库·tdengine·涛思数据