1. 概述

本文将系统介绍在 Java 中从 ArrayList 删除元素的多种方式。我们先准备一个基础方法,用于生成一个包含运动项目的列表:

List<String> getSportList() {
    List<String> sports = new ArrayList<>();
    sports.add("Football");
    sports.add("Basketball");
    sports.add("Baseball");
    sports.add("Boxing");
    sports.add("Cycling");
    return sports;
}

接下来,我们将基于这个列表,演示各种删除元素的技巧。


2. 使用 ArrayList#remove 方法

ArrayList 提供了两个重载的 remove 方法:

remove(int index) —— 根据索引删除
remove(Object o) —— 根据元素值删除(仅删除第一次出现的位置)

下面我们逐一说明。

2.1 按索引删除

调用 remove(index) 会删除指定位置的元素,并将后续元素左移,同时返回被删除的元素。这是最直接也最高效的方式之一。

示例:

List<String> sports = getSportList();
assertThat(sports).hasSize(5).contains("Basketball");

sports.remove(1); // 删除索引为1的元素 "Basketball"
assertThat(sports).hasSize(4).doesNotContain("Basketball");

⚠️ 注意:索引从 0 开始,删除后列表会自动调整大小。


2.2 按元素值删除

通过传入具体对象调用 remove(Object),会删除该元素第一次出现的位置。如果没有匹配项,列表不变。

List<String> sports = getSportList();
assertThat(sports).hasSize(5).contains("Basketball");

sports.remove("Basketball");

assertThat(sports).hasSize(4).doesNotContain("Basketball");

✅ 这种方式适合你知道要删谁,但不知道它在哪的情况。
❌ 但注意:只删第一个匹配项,重复元素需特殊处理。


2.3 删除最后一个元素

日常开发中,“删末尾”是个高频操作。传统做法是结合 size()remove(index)

List<String> sports = getSportList();
List<String> expected = List.of("Football", "Basketball", "Baseball", "Boxing");

sports.remove(sports.size() - 1); // 删除最后一个
assertThat(sports).isEqualTo(expected);

但从 JDK 21 起List 接口新增了一个更语义化的 API:removeLast()

List<String> sports = getSportList();
List<String> expected = List.of("Football", "Basketball", "Baseball", "Boxing");

sports.removeLast();
assertThat(sports).isEqualTo(expected);

✅ 推荐:如果你的项目已升级到 JDK 21+,removeLast() 更简洁、可读性更强,是首选方式。
⚠️ 注意:调用前需确保列表非空,否则抛 NoSuchElementException


3. 遍历过程中删除元素

这是个经典“踩坑”场景:直接在 for-each 循环中调用 list.remove() 会触发 ConcurrentModificationException

正确做法是使用 Iterator,并调用其 remove() 方法:

List<String> sports = getSportList();
assertThat(sports).hasSize(5).contains("Basketball");

Iterator<String> iterator = sports.iterator();
while (iterator.hasNext()) {
    if (iterator.next().equals("Basketball")) {
        iterator.remove(); // 安全删除
        break;
    }
}
assertThat(sports).hasSize(4).doesNotContain("Basketball");

✅ 关键点:

  • 必须使用 Iterator.remove(),而不是 List.remove()
  • iterator.remove() 是唯一允许在遍历时安全删除的方式
  • 调用前必须先调用 next()

4. 使用 ArrayList#removeIf(JDK 8+)

从 JDK 8 开始,Collection 接口引入了 removeIf(Predicate) 方法,支持基于条件批量删除,代码更函数式、更简洁。

List<String> sports = getSportList();
assertThat(sports).hasSize(5).contains("Basketball");

sports.removeIf(p -> p.equals("Basketball"));

assertThat(sports).hasSize(4).doesNotContain("Basketball");

✅ 优势:

  • 一行代码搞定条件删除
  • 内部自动处理迭代器,无需手动写循环
  • 支持复杂条件,比如 s -> s.startsWith("B")

📌 示例:删除所有以 "B" 开头的运动

sports.removeIf(s -> s.startsWith("B"));
// 结果:["Football", "Cycling"]

⚠️ 注意:removeIf立即执行的,会直接修改原列表。


5. 总结

方法 适用场景 JDK 版本 是否推荐
remove(index) 已知索引 所有版本
remove(object) 删除首个匹配元素 所有版本
removeLast() 删除末尾元素 JDK 21+ ✅ 推荐新项目使用
Iterator.remove() 遍历中按条件删除 所有版本 ✅ 必须用它
removeIf(predicate) 条件批量删除 JDK 8+ ✅ 强烈推荐

📌 最佳实践建议:

  • 能用 removeIf 就不用手动遍历
  • JDK 21+ 优先使用 removeLast()
  • 遍历时删除必须用 Iterator
  • 注意空指针和越界问题

所有示例代码已托管至 GitHub:https://github.com/baeldung/core-java-modules/tree/master/core-java-collections-array-list


原始标题:Removing an Element From an ArrayList | Baeldung