Stream流式操作需遵循“先筛后算、链式清晰、副作用归零”原则:filter必须前置防空指针,map/lambda须无状态无副作用,数值计算优先用mapToInt等原始类型流,取前N个应filter+limit而非takeWhile。

Stream 流式操作的核心在于“先筛后算、链式清晰、副作用归零”。用对了,代码简洁又高效;顺序或逻辑一错,轻则结果不准,重则空指针或性能骤降。
过滤必须前置:filter() 要比 map() 先出现
遇到 null 或非法值时,把 filter() 放在最前面能直接跳过后续计算。比如处理一批可能含 null 的字符串:
- ✅ 正确写法:list.stream().filter(Objects::nonNull).map(String::length).collect(Collectors.toList()) —— null 被提前剔除,不会调用 null.length()
- ❌ 错误写法:list.stream().map(String::length).filter(n -> n > 0).collect(...) —— 遇到 null 就抛 NullPointerException
尤其在处理数据库查询结果、HTTP 响应或 CSV 解析数据时,原始数据常带脏数据,filter() 前置是第一道安全阀。
映射要干净:lambda 必须无状态、无副作用
map() 和 filter() 中的 lambda 只负责转换或判断,不能修改外部变量、发请求、写日志或更新静态计数器。
- ⚠️ 危险示例:map(s -> { cache.put(s, s.toUpperCase()); return s; }) —— 并行流下缓存可能错乱、重复写入
- ✅ 替代方案:纯函数式转换 map(s -> s.trim().toUpperCase());副作用操作统一交给 peek()(仅调试)或最终的 forEach()
数值计算优先用原始类型流
如果只做求和、平均、最大最小等数值聚合,用 mapToInt() 可避免 Integer 装箱开销:
- ✅ 推荐:students.stream().mapToInt(Student::getAge).sum()
- ⚠️ 注意:返回的是 IntStream,不能再直接接 map();若后续还需对象操作,强行 boxed() 会抵消优化效果
- ? 实测提示:Android 低版本或老 JVM(如 Java 8u40 前)对原始类型 Stream 优化有限,别盲目替换
取前 N 个要分清 limit() 和 takeWhile()
两者语义完全不同:
- limit(5):取整个流中满足条件的前 5 个元素(需配合 filter 使用)
- takeWhile(s -> !s.isEmpty()):从头开始取,一旦遇到空串就立刻停止,不管后面还有没有非空字符串
例如“取前 5 个非空字符串”,必须写成:list.stream().filter(s -> !s.isEmpty()).limit(5),而不是 takeWhile。


















