应使用filter(objects::nonnull)而非x -> x != null,因其语义清晰、类型安全、兼容泛型擦除,且避免下游npe;误写filter(objects.nonnull)会编译失败。

为什么 Stream.filter 直接用 null 判断会出错
因为 Stream.filter 接收的是 Predicate<t></t>,而 list.stream().filter(x -> x != null) 看似可行,但一旦 T 是基本类型包装类(比如 Integer),或后续操作调用了 map 产生中间 null,就容易在 forEach 或 collect 阶段触发 NullPointerException——不是 filter 挂了,是下游没防住。
更关键的是:手动写 x != null 缺乏语义表达,团队协作时不如明确调用判空工具。
-
Objects.nonNull是 JDK 7+ 提供的静态方法,专为这类场景设计,可读性高、无副作用 - 它返回
boolean,和Predicate的test方法签名完全匹配,能直接方法引用 - 注意:它只判断对象引用是否为
null,不处理空字符串、空集合等“逻辑空值”
Objects.nonNull 在 filter 中的正确写法
最简也最推荐的方式是方法引用:filter(Objects::nonNull)。它比 filter(x -> Objects.nonNull(x)) 更简洁,字节码层面也略优(少一次 lambda 实例化)。
常见误写:filter(Objects.nonNull) —— 这是语法错误,编译不过,因为 nonNull 是静态方法,必须带 :: 才能作为方法引用。
立即学习“Java免费学习笔记(深入)”;
- 适用于任意泛型类型:无论是
List<string></string>、List<user></user>还是List<integer></integer> - 如果 list 本身为
null,调用stream()前就要处理,Objects.nonNull对 list 本身无效 - 示例:
List<String> list = Arrays.asList("a", null, "b", null); List<String> filtered = list.stream() .filter(Objects::nonNull) .collect(Collectors.toList()); // ["a", "b"]
和 Collection.removeIf 的区别在哪
有人图省事想用 list.removeIf(Objects::isNull),这确实能原地删空值,但它和 Stream.filter 不是一个维度的事:前者修改原集合,后者生成新流。选哪个取决于你是否允许副作用。
- 需要不可变结果、链式处理(比如 filter → map → reduce),必须用
Stream.filter - 内存敏感场景下,
removeIf避免创建新集合,但会改变原 list,可能影响其他引用 -
removeIf(Objects::isNull)和filter(Objects::nonNull)逻辑等价,但别混用:一个删null,一个留非null,方向相反 - 如果 list 是
Arrays.asList()返回的固定大小列表,removeIf会抛UnsupportedOperationException
容易被忽略的边界:泛型擦除与原始类型
当 list 声明为原始类型(如 List list = new ArrayList();),泛型信息丢失,stream() 返回的是 Stream<object></object>,此时 filter(Objects::nonNull) 依然安全,但后续 map 或 collect 可能因类型不匹配报 ClassCastException。
- 永远优先使用带泛型的声明:
List<string></string>而不是List - 如果上游只能给原始类型,过滤后建议立即用
collect(Collectors.toList())得到具体类型,避免延迟执行时出问题 -
Objects.nonNull本身不依赖泛型,所以即使泛型擦除,它对引用的判空逻辑也不受影响
filter(Objects::nonNull),而是忘了 upstream 可能已经塞进 null,或者 downstream 没检查 Optional 是否存在就直接 get()。










