1. 概述

在本篇文章中,我们将介绍 四种不同的方式从 Java 的 Collections 中移除满足特定条件(谓词)的元素

同时,我们也会指出一些使用过程中需要注意的细节和潜在问题。

2. 定义集合

我们先介绍两种会直接修改原始数据结构的方法。然后再来看两种不会修改原集合、而是创建一个新集合的方式。

在示例中,我们都使用如下这个 Collection<String>

Collection<String> names = new ArrayList<>();
names.add("John");
names.add("Ana");
names.add("Mary");
names.add("Anthony");
names.add("Mark");

3. 使用 Iterator 删除元素

Java 的 Iterator 提供了遍历并逐个删除集合中元素的能力。

要实现这一点,首先需要调用集合的 iterator() 方法获取迭代器实例,然后通过 next() 遍历元素,并使用 remove() 删除符合条件的元素:

Iterator<String> i = names.iterator();

while(i.hasNext()) {
    String e = i.next();
    if (e.startsWith("A")) {
        i.remove();
    }
}

虽然这种方式简单直接,但也有几个需要注意的地方:

优点:

  • 适用于所有实现了 Iterator 的集合类型

⚠️ 注意:

  • 如果在遍历过程中直接对集合进行结构性修改(比如用 remove() 之外的方式),可能会抛出 ConcurrentModificationException
  • 必须先调用 next() 才能调用 remove(),否则会抛异常
  • 对于不同集合类型,remove() 的实现机制不同:
    • ArrayList.Iterator 删除元素后会将后续元素左移
    • LinkedList.Iterator 只需调整指针即可
    • 因此,在频繁删除操作下,LinkedList 性能更优

4. Java 8 中的 Collection.removeIf()

Java 8 在 Collection 接口中新增了一个方法 —— removeIf(Predicate),它提供了一种更加简洁的方式来根据谓词删除元素:

names.removeIf(e -> e.startsWith("A"));

相比使用 IteratorremoveIf()ArrayListLinkedList 上表现更为一致:

亮点:

  • 内部做了优化处理
  • ArrayList 覆盖了默认实现,采用两轮遍历策略:
    1. 第一轮标记匹配的元素
    2. 第二轮统一删除并移动其余元素

这样避免了多次移动数组元素带来的性能损耗。

5. Java 8 引入的 Stream

Java 8 带来了强大的 Stream API,以及配套的 Collectors 工具类。

大多数 Stream 操作并不会修改源集合,而是生成新的集合副本。

5.1. 使用 Stream 过滤元素

使用 Stream 来“删除”元素其实是一种过滤操作,非常简单粗暴:

Collection<String> filteredCollection = names
  .stream()
  .filter(e -> !e.startsWith("A"))
  .collect(Collectors.toList());

这种方式不会影响原始集合,适合在不可变编程或函数式风格中使用。

⚠️ 注意内存开销:

  • 创建了新的集合对象,会增加内存占用
  • 不适合大数据量下的频繁操作

5.2. 使用 Collectors.partitioningBy

有时候我们不仅想得到不匹配的元素,还想保留匹配的部分。这时候就可以使用 *Collectors.partitioningBy*:

Map<Boolean, List<String>> classifiedElements = names
    .stream()
    .collect(Collectors.partitioningBy((String e) -> 
      !e.startsWith("A")));

String matching = String.join(",",
  classifiedElements.get(true));
String nonMatching = String.join(",",
  classifiedElements.get(false));

该方法返回一个 Map,只包含两个键:truefalse,分别对应满足条件和不满足条件的元素列表。

6. 小结

本文介绍了多种在 Java 集合中删除元素的方法及其适用场景:

方法 是否修改原集合 性能特点 适用场景
Iterator.remove() ✅ 是 LinkedList 更快 精确控制流程
Collection.removeIf() ✅ 是 统一优化处理 简洁表达删除逻辑
Stream.filter() + collect() ❌ 否 新建集合 不可变/函数式编程
Collectors.partitioningBy() ❌ 否 分类结果 同时需要两类数据

你可以在这里找到完整代码示例:GitHub 项目地址


原始标题:Removing Elements from Java Collections