stream().filter()是最常用且安全的筛选方式,返回新stream需collect()才能获取结果;removeif()直接修改原集合但返回布尔值表示是否删除;禁用for-each中remove(),应使用iterator.remove()或批量removeall()。

用 stream().filter() 是最常用也最安全的方式
Java 8 引入的 Stream API 让集合筛选变得声明式且不易出错。只要集合不是 null,stream().filter() 就能直接用,底层自动处理空值(除非你在 lambda 里自己抛)。
常见错误是误以为 filter() 会修改原集合——它不会,返回的是新 Stream,必须接 collect() 才能得到结果集合:
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> filtered = list.stream()
.filter(s -> s.length() > 5)
.collect(Collectors.toList()); // 忘记这步就什么也拿不到
- 如果原集合是
ArrayList,用Collectors.toCollection(ArrayList::new)可保持类型 - 对
Map筛选,得先转成entrySet().stream(),再按Map.Entry::getKey或::getValue过滤 - 性能上,小集合(filter 里做 IO 或重计算
别在 for-each 循环里直接 remove()
这是新手高频翻车点:一边遍历一边调 list.remove(),会跳过下一个元素,甚至抛 ConcurrentModificationException。
正确做法只有两个:
立即学习“Java免费学习笔记(深入)”;
- 用
Iterator的remove()方法(仅适用于List、Set等支持迭代器移除的集合) - 收集待删元素,循环结束后再批量
removeAll()
示例(安全删除):
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
if (it.next() % 2 == 0) it.remove(); // ✅ 安全
}
removeIf() 适合就地过滤,但注意它的返回值含义
removeIf(Predicate) 是 Collection 接口的方法,直接修改原集合,语义清晰,代码短。
容易被忽略的是它的返回值:boolean,表示「是否有元素被删掉」,不是过滤后剩下的数量,也不是是否成功执行。很多人误把它当计数用:
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
boolean changed = names.removeIf(s -> s.length() < 4); // Bob 被删,changed == true
// names 现在是 ["Alice", "Charlie"],但 changed 不是 1
- 只适用于支持修改的集合(
ArrayList、LinkedList、HashSet等),Arrays.asList()返回的列表不支持,会抛UnsupportedOperationException - 内部使用
Iterator.remove(),所以线程不安全,多线程环境下需额外同步
第三方库如 Guava 的 Iterables.filter() 已基本淘汰
Guava 早期提供 Iterables.filter() 和 Lists.newArrayList() 组合来筛选,但现在既没 Stream 简洁,又不支持并行,还引入额外依赖。
除非你卡在 Java 7 且无法升级,否则没必要用。更关键的是:Iterables.filter() 返回的是视图(view),底层仍引用原集合,如果原集合后续被修改,视图行为可能出人意料。
比如:
List<Integer> source = Lists.newArrayList(1, 2, 3); Iterable<Integer> view = Iterables.filter(source, Predicates.greaterThan(1)); source.add(0); // 修改原集合 // view 遍历时可能包含或不包含 0,取决于实现细节 —— 这种隐式耦合很难调试
真正要小心的,是把「筛选逻辑」和「集合可变性」混在一起:用 removeIf() 前先确认集合是否允许修改;用 stream().filter() 时别忘了 collect();所有涉及 Iterator 的操作,都得盯紧「谁在删、谁在遍历、是否同一线程」。










