杰瑞科技汇

Java for循环遍历List有几种方式?

下面我将从最传统的方式开始,逐步介绍到现代 Java 中最推荐的方式,并附上代码示例和优缺点分析。

Java for循环遍历List有几种方式?-图1
(图片来源网络,侵删)

准备工作:创建一个示例 List

为了演示,我们先创建一个 List<String> 并添加一些元素,这个 List 将在所有示例中复用。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ListIterationExample {
    public static void main(String[] args) {
        // 创建一个包含水果名称的 List
        List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
    }
}

传统的 for 循环 (索引遍历)

这是最早期、最经典的遍历方式,通过索引来访问 List 中的每一个元素。

代码示例:

List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("--- 方法一:传统的 for 循环 (索引遍历) ---");
for (int i = 0; i < fruits.size(); i++) {
    String fruit = fruits.get(i);
    System.out.println("水果: " + fruit);
}

优点:

Java for循环遍历List有几种方式?-图2
(图片来源网络,侵删)
  1. 直观易懂:对于初学者来说,逻辑非常清晰,就是通过下标一个一个地取。
  2. 随机访问:可以在循环体内部通过 i 来随机访问 List 中的任何元素,fruits.get(0)
  3. 兼容性最好:适用于所有版本的 Java 和所有实现了 List 接口的集合类。

缺点:

  1. 代码冗长:需要声明并管理索引变量 i
  2. 容易出错List 为空,fruits.size() 为 0,循环不会执行,这是安全的,但如果在循环体内修改了 List 的大小(例如删除或添加元素),可能会导致 IndexOutOfBoundsException 或逻辑错误。
  3. 性能问题:对于 LinkedList 这样的非随机访问列表,每次调用 get(i) 都需要从头部或尾部遍历,时间复杂度为 O(n),整个循环的时间复杂度会达到 O(n²),而对于 ArrayListget(i) 的时间复杂度是 O(1),所以整体是 O(n)。

增强型 for 循环 (For-Each 循环)

Java 5 引入了这种语法,也称为 "for-each" 循环,它极大地简化了遍历集合和数组的代码。

代码示例:

List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法二:增强型 for 循环 (For-Each) ---");
for (String fruit : fruits) {
    System.out.println("水果: " + fruit);
}

优点:

Java for循环遍历List有几种方式?-图3
(图片来源网络,侵删)
  1. 代码简洁:无需关心索引,代码更易读、更优雅。
  2. 不易出错:自动处理了迭代过程,避免了索引越界的风险。
  3. 性能良好:底层使用迭代器实现,对于 ArrayListLinkedList,其时间复杂度都是 O(n),性能稳定。

缺点:

  1. 无法获取索引:如果你需要知道当前元素是第几个(索引),这种方式不方便。
  2. 不能安全地修改集合:在遍历过程中,不能直接使用 list.remove()list.add() 方法来修改 List,否则会抛出 ConcurrentModificationException 异常。(见下文关于迭代器删除的说明)

使用 Iterator (迭代器)

Iterator 是 Java 集合框架中专门用于遍历和移除元素的对象,它是“ fail-fast ”机制(快速失败)的基础。

代码示例:

List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法三:使用 Iterator ---");
// 1. 获取迭代器
Iterator<String> iterator = fruits.iterator();
// 2. 使用 while 循环遍历
while (iterator.hasNext()) {
    String fruit = iterator.next();
    System.out.println("水果: " + fruit);
    // 示例:如果水果是 "Cherry",则移除它
    if ("Cherry".equals(fruit)) {
        // 3. 使用迭代器的 remove() 方法安全地移除元素
        iterator.remove();
    }
}
System.out.println("移除 Cherry 后的 List: " + fruits);

优点:

  1. 唯一安全的方式:是唯一一种可以在遍历过程中安全地删除元素的方式,必须调用 iterator.remove(),而不是 list.remove()
  2. 通用性强:适用于所有实现了 Iterable 接口的集合,不仅仅是 List
  3. fail-fast 机制:如果在迭代过程中,除了通过 iterator.remove() 之外,任何其他方式修改了集合(例如另一个线程修改了集合,或者代码中直接调用了 list.remove()),迭代器会立即检测到并抛出 ConcurrentModificationException,帮助开发者尽早发现并发修改的问题。

缺点:

  1. 代码相对繁琐:需要先获取迭代器,然后使用 while 循环,代码比 for-each 循环长。
  2. 同样无法获取索引

Java 8+ 的 forEach 和 Lambda 表达式

Java 8 引入了 Stream API 和 Lambda 表达式,提供了函数式风格的遍历方式。

代码示例:

List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法四:Java 8+ 的 forEach 和 Lambda ---");
// 使用 Lambda 表达式
fruits.forEach(fruit -> System.out.println("水果: " + fruit));
// 如果方法体只有一行,可以更简洁
// fruits.forEach(System.out::println);

优点:

  1. 非常简洁:代码行数最少,可读性高,特别是与方法引用(System.out::println)结合使用时。
  2. 函数式编程风格:更符合现代编程范式,易于与其他 Stream 操作链式调用。
  3. 线程安全(外部迭代)forEach 是一种“外部迭代”,由你控制循环过程,因此不存在并发修改异常的问题,如果需要在多线程环境下遍历,可以确保安全。

缺点:

  1. 无法在遍历中修改集合forEach 的操作是“只读”的,你不能在 Lambda 表达式中调用 remove() 方法来修改 List
  2. 无法提前终止:不像 for 循环可以 breakforEach 会遍历所有元素,如果需要提前终止,可以使用 StreamanyMatch()allMatch() 等操作。
  3. 需要 Java 8 或更高版本

Java 8+ 的 Stream API

Stream API 提供了更强大的功能,如过滤、映射、聚合等,遍历只是其众多功能之一。

代码示例:

List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"));
System.out.println("\n--- 方法五:Java 8+ 的 Stream API ---");
// 过滤出长度大于 5 的水果,并打印
fruits.stream()
      .filter(fruit -> fruit.length() > 5)
      .forEach(fruit -> System.out.println("长水果名: " + fruit));

优点:

  1. 功能极其强大:可以进行复杂的链式操作,如 filter(), map(), sorted(), reduce() 等。
  2. 支持并行处理:只需将 .stream() 换成 .parallelStream(),就可以轻松实现并行计算,利用多核 CPU 提升性能。
  3. 代码声明式:你只需要“声明”你想做什么,而不必关心“怎么做”。

缺点:

  1. 性能开销:对于简单的遍历,创建 Stream 对象会有一定的性能开销。
  2. 无法修改集合:和 forEach 一样,Stream 操作也是只读的。
  3. 需要 Java 8 或更高版本

总结与如何选择

方法 代码示例 优点 缺点 适用场景
传统 for 循环 for (int i=0; i<list.size(); i++) 直观,可随机访问,兼容性好 冗长,易出错,LinkedList 性能差 需要索引或随机访问时;旧项目兼容。
增强型 for 循环 for (T item : list) 简洁,易读,不易出错 无法获取索引,遍历中不能修改集合 日常遍历的首选,绝大多数情况都适用。
Iterator while (it.hasNext()) { it.next(); } 唯一安全地删除元素,fail-fast 代码繁琐,无法获取索引 需要在遍历中删除或修改元素时
forEach + Lambda list.forEach(item -> ...) 极其简洁,函数式风格 无法修改集合,无法提前终止 现代Java项目,代码简洁性要求高。
Stream API list.stream().filter(...).forEach(...) 功能强大,支持并行处理 性能开销,无法修改集合 需要对数据进行复杂操作(过滤、排序等)时。

推荐选择:

  • 如果只是简单地遍历 List 中的每一个元素:使用 增强型 for 循环,这是最平衡、最通用的选择。
  • 如果在遍历过程中需要删除元素:使用 Iterator,并务必调用 iterator.remove()
  • 如果使用的是 Java 8+ 且追求代码的简洁和函数式风格:使用 list.forEach()
  • 如果需要对 List 进行更复杂的处理(如筛选、转换、聚合):使用 Stream API
分享:
扫描分享到社交APP
上一篇
下一篇