杰瑞科技汇

Java遍历List时如何正确删除元素?

核心要点

最重要的一点是:在遍历一个集合时,绝对不要使用 for (int i = 0; i < list.size(); i++) 这样的循环来删除元素。

Java遍历List时如何正确删除元素?-图1
(图片来源网络,侵删)

这样做会导致 IndexOutOfBoundsException(索引越界异常),因为删除元素后,Listsize() 会变小,而你的循环变量 i 仍然会继续递增,直到达到或超过原始的 size,从而导致访问不存在的索引。


正确的删除方式

下面我们介绍几种正确且常用的方法,并分析它们的适用场景。

使用 Iterator (最经典、最安全的方式)

Iterator 是专门为集合遍历而设计的,它提供了一个 remove() 方法,可以在遍历时安全地删除当前元素。

原理: Iterator 维护了一个指向当前元素的“游标”,当你调用 iterator.next() 时,游标会移动到下一个元素,而 iterator.remove() 方法会删除上一次 next() 返回的元素,并更新 Iterator 的内部状态,确保后续遍历的正确性。

Java遍历List时如何正确删除元素?-图2
(图片来源网络,侵删)

示例代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class RemoveWithIterator {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");
        System.out.println("删除前: " + names);
        // 获取迭代器
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            // 假设我们要删除所有名字长度小于等于3的人
            if (name.length() <= 3) {
                // 使用迭代器的 remove 方法,而不是 List 的 remove
                iterator.remove();
            }
        }
        System.out.println("删除后: " + names);
    }
}

输出:

删除前: [Alice, Bob, Charlie, David, Eve]
删除后: [Alice, Charlie, David, Eve]

优点:

  • 线程安全:在单线程环境下,Iteratorremove() 是原子操作,不会导致并发问题。
  • 逻辑清晰remove() 操作紧随 next() 之后,代码意图明确。
  • 兼容性好:适用于所有 Collection 的子类,如 Set, List 等。

使用 Java 8+ 的 Stream API (函数式风格,代码简洁)

如果你使用的是 Java 8 或更高版本,可以使用 Stream API 来过滤元素,这是一种更现代、更函数式的方法。

Java遍历List时如何正确删除元素?-图3
(图片来源网络,侵删)

原理: stream() 方法将 List 转换为一个流,filter() 方法根据条件保留符合条件的元素,collect(Collectors.toList()) 将过滤后的流重新收集成一个 List

示例代码:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class RemoveWithStream {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");
        System.out.println("删除前: " + names);
        // 使用 Stream API 过滤元素
        // 注意:这会创建一个新的 List,而不是在原 List 上修改
        List<String> filteredNames = names.stream()
                                         .filter(name -> name.length() > 3)
                                         .collect(Collectors.toList());
        System.out.println("删除后 (新List): " + filteredNames);
        System.out.println("原始List (未改变): " + names); // 原始List没有被修改
        // 如果想修改原始 List,可以重新赋值
        names = names.stream()
                     .filter(name -> name.length() > 3)
                     .collect(Collectors.toList());
        System.out.println("原始List (重新赋值后): " + names);
    }
}

输出:

删除前: [Alice, Bob, Charlie, David, Eve]
删除后 (新List): [Alice, Charlie, David, Eve]
原始List (未改变): [Alice, Bob, Charlie, David, Eve]
原始List (重新赋值后): [Alice, Charlie, David, Eve]

优点:

  • 代码简洁:一行代码就能完成复杂的过滤逻辑,可读性高。
  • 函数式编程:不修改原始数据(除非重新赋值),避免了副作用,更易于测试和维护。
  • 功能强大:可以轻松进行更复杂的操作,如 map, sorted, forEach 等。

缺点:

  • 性能开销:对于非常大的 List,创建流和收集新 List 会带来一定的性能开销。
  • 不直接修改原集合:它生成一个新集合,而不是在原集合上操作。

使用 removeIf 方法 (Java 8+,最便捷的方式)

这是 Java 8 引入的一个非常方便的 List (以及 Set, Collection) 实例方法,是 Iterator 模式的完美封装。

原理: removeIf 接受一个 Predicate (谓词,一个返回布尔值的函数) 作为参数,它会遍历 List,并删除所有使 Predicate 返回 true 的元素,其内部实现就是基于 Iterator

示例代码:

import java.util.ArrayList;
import java.util.List;
public class RemoveWithRemoveIf {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");
        System.out.println("删除前: " + names);
        // 使用 removeIf 方法,直接在原 List 上删除
        names.removeIf(name -> name.length() <= 3);
        System.out.println("删除后: " + names);
    }
}

输出:

删除前: [Alice, Bob, Charlie, David, Eve]
删除后: [Alice, Charlie, David, Eve]

优点:

  • 极其简洁:一行代码,意图明确。
  • 高效安全:内部使用 Iterator 实现,是线程安全的(在单线程遍历期间)。
  • 直接修改原集合:不像 Stream 那样创建新对象,直接在原始 List 上操作。

缺点:

  • 仅适用于 Java 8 及以上版本。

方法对比与选择指南

方法 优点 缺点 适用场景
Iterator - 最经典、最安全
- 线程安全
- 适用于所有 Collection
- 代码相对冗长 - Java 8 之前的版本
- 需要在遍历中执行复杂逻辑(除了删除)
- 需要显式控制遍历过程
Stream API - 代码最简洁
- 函数式风格,无副作用
- 功能强大,可链式调用
- 不直接修改原集合
- 有性能开销(创建流和中间集合)
- Java 8+
- 需要对集合进行转换、映射等多种操作
- 需要生成一个新集合而不是修改原集合
removeIf - 最便捷
- 高效安全
- 直接修改原集合
- 仅限 Java 8+ - Java 8+ 的首选方案
- 仅需根据条件删除元素,无其他复杂操作
  1. 绝对不要for-i 循环中删除 List 元素。
  2. Java 8 之前:使用 Iterator 是唯一安全且推荐的方式。
  3. Java 8 及之后
    • 如果只是简单删除,直接使用 list.removeIf(condition),这是最简单、最直接的方法。
    • 如果需要在删除的同时做其他处理(比如打印被删除的元素),或者代码逻辑更复杂,使用 Iterator 仍然是一个很好的选择。
    • 如果需要生成一个过滤后的新 List 而不是修改原 List,或者需要进行更复杂的流式处理,使用 Stream API 是最佳选择。
分享:
扫描分享到社交APP
上一篇
下一篇