用stream.filter(objects::nonnull)去null正确但不修改原集合,必须.collect()获取新列表;removeif(objects::isnull)才原地删除;应优先治理null源头而非下游过滤。

用 Stream.filter(Objects::nonNull) 去 null 是对的,但要注意它不修改原集合
这个写法本身没问题,Objects::nonNull 是最简洁安全的判空谓词,但新手常误以为它会“原地清理”List。实际上,filter() 返回的是全新 Stream,必须显式收集——漏掉这步就等于什么都没做。
- 必须接
.collect(Collectors.toList())或.toList()(Java 16+)才能得到新列表 - 原
List保持不变,包括它的类型(比如ArrayList或不可变集合) - 如果原集合是
Collections.unmodifiableList()这类不可变对象,filter仍能用,但结果一定是新对象
List<String> original = Arrays.asList("a", null, "b", null);
List<String> cleaned = original.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList()); // ← 这行不能省
removeIf(Objects::isNull) 才是真·原地删除
想直接改原集合?用 removeIf。它内部遍历并调用 Iterator.remove(),比先转流再收集更直白、省内存,尤其对大列表。
- 只适用于支持修改的集合(如
ArrayList、LinkedList),对Arrays.asList()返回的固定大小列表会抛UnsupportedOperationException - 参数用
Objects::isNull(不是nonNull),语义更贴切:删掉“是 null”的元素 - 性能上避免了新建 Stream 和中间对象,GC 压力小一点
List<String> list = new ArrayList<>(Arrays.asList("a", null, "b", null));
list.removeIf(Objects::isNull); // list 现在是 ["a", "b"]
遇到 NullPointerException 别急着加 filter,先查源头
频繁在下游用 filter(Objects::nonNull) 往往说明上游数据质量有问题。比如从 JSON 解析、数据库查询或外部 API 拿到的字段本该非空却为 null,硬过滤只是掩盖问题。
- 数据库字段是否设了
NOT NULL约束?JPA 实体里对应属性有没有@NotNull或默认值? - JSON 反序列化时,用 Jackson 的
@JsonInclude(JsonInclude.Include.NON_NULL)控制序列化行为,比反序列化后再过滤更早拦截 - 如果是自定义构造逻辑,考虑用
Optional显式表达可能为空,而不是塞 null 进集合
注意 null 和空字符串、空白字符串不是一回事
Objects::nonNull 只管 null,对 "" 或 " " 完全没反应。业务上经常要一并清理,得手动组合条件。
立即学习“Java免费学习笔记(深入)”;
- 简单场景:用
filter(s -> s != null && !s.trim().isEmpty()) - 更严谨:用
StringUtils.isNotBlank()(Apache Commons Lang),它同时处理null、空串、纯空白 - 别写成
s.trim().length() > 0——s为null时会 NPE,顺序错了
List<String> mixed = Arrays.asList("a", null, "", " ", "b");
List<String> clean = mixed.stream()
.filter(Objects::nonNull)
.filter(s -> !s.trim().isEmpty())
.collect(Collectors.toList()); // ["a", "b"]
Java 集合去 null 表面简单,真正麻烦的是 null 出现在哪一层:是数据源本就不该有,还是中间加工时意外引入,又或者你其实该用 Optional 重构接口。选 filter 还是 removeIf 只是表象,源头治理才最难下手。










