filter没生效是因为stream惰性求值,必须调用collect等终端操作才能触发;map中空指针需提前filter或lambda内判空;reduce的identity须为中性元素;避免在lambda中做io或重复计算。

filter 之后为什么没生效?检查是否忘了 collect
Java 8 的 Stream 是惰性求值的,调用 filter、map 这些中间操作不会立刻执行,也不会修改原集合。常见错误是写了 list.stream().filter(...) 就以为过滤完了,结果打印原集合发现毫无变化。
- 必须显式触发终端操作,最常用的是
collect(Collectors.toList()) - 如果只想要一个结果,用
findFirst()或findAny();要判断是否存在,用anyMatch(),别直接丢掉返回值 - 误用
forEach做“过滤后处理”很危险——它不返回新流,也无法链式继续操作,且无法中断或抛异常传播
示例:List<string> filtered = list.stream().filter(s -> s.length() > 3).collect(Collectors.toList());</string>
map 转换时 NullPointerException 怎么避?
map 函数内部若对 null 元素调用方法(比如 s.toUpperCase()),会立即抛 NullPointerException。这不是 Stream 的 bug,而是你传入的 lambda 没做空防护。
- 提前过滤:在
map前加filter(Objects::nonNull) - 在 lambda 内部判空:比如
map(s -> s == null ? null : s.trim()) - 用
Optional包装再 map(适合复杂逻辑),但别为了用 Optional 而强行套,增加可读负担 - 注意
Collectors.toList()不拒绝 null 元素,而Collectors.toSet()会因 null 报NullPointerException
reduce 累加出错:identity 和 accumulator 不匹配
reduce(identity, accumulator, combiner) 三参数版本常被误用。典型问题是 identity 值选错,导致第一次 accumulator 计算就崩,或者并发场景下 combiner 逻辑和 accumulator 不一致。
立即学习“Java免费学习笔记(深入)”;
- identity 必须是“中性元素”,比如求和用
0,字符串拼接用"",集合合并用new ArrayList() - accumulator 必须满足:
accumulator.apply(identity, t) == t,否则 reduce 行为不可预测 - 多线程流(
parallelStream())必须提供 combiner,且 combiner 和 accumulator 语义要等价,比如都用+,不能一个用+、一个用StringBuilder.append() - 简单累加优先用
sum()、count()、max()等内置终端操作,它们更安全、可读性更好
示例(安全):int sum = numbers.stream().reduce(0, Integer::sum);
性能陷阱:不要在 filter/map 里做 IO 或重计算
Stream 的每个元素都会执行一次 lambda。如果在 filter 里查数据库、读文件、解析 JSON,或反复 new 大对象,性能会断崖式下跌,而且难以定位。
- IO 操作一律提到流外:先批量获取数据,再用 Stream 处理内存对象
- 重复计算提取成变量:比如
map(s -> s.substring(0, Math.min(10, s.length())))中的s.length()别重复调两次 - 避免在 lambda 里写复杂逻辑块,拆成独立方法,方便单元测试和复用
-
parallelStream()不是银弹:小集合(
真正容易被忽略的是:Stream 操作链越长,调试越困难。一旦某步出错,堆栈里看不到原始集合来源,也难打点。生产环境建议关键路径上保留一两个 peek(System.out::println)(上线前删掉),比靠猜强得多。










