java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java集合遍历指南

Java集合遍历全指南(迭代器&增强for及列表迭代器)

作者:黎雁

本文详尽介绍了Java集合遍历的5种方式:迭代器、增强for、列表迭代器、普通for及Lambda表达式,通过代码示例和原理解析,帮助读者彻底掌握集合遍历的核心知识,避免常见错误如并发修改异常和指针复位问题,适合Java初学者及面试复习,需要的朋友可以参考下

文章摘要

迭代器与增强 for 循环

一、迭代器遍历(Iterator 接口)

迭代器是 不依赖索引 的通用遍历方式,适配所有 Collection 系列集合(List、Set 均可用),是集合专属的标准遍历工具,无需关心集合底层存储结构,只需专注“遍历取元素”。

获取迭代器

Collection 集合通过专属方法获取迭代器对象,默认指针指向集合 0 索引位置:

方法名说明
Iterator<E> iterator()返回迭代器对象,默认指向当前集合 0 索引

迭代器核心成员方法

迭代器的用法固定,核心就是“判断有没有 → 取元素 → (可选)删元素”三步,常用方法如下:

方法名说明
boolean hasNext()判断当前指针位置是否有元素,有则返回 true,无则返回 false(避免越界)
E next()获取当前指针位置的元素,并将迭代器指针自动移向下一个位置
default void remove()删除上一次 next() 方法返回的元素(仅迭代器遍历期间可用)

迭代器四大关键细节(必背避坑,少踩90%错误)

  1. 边界异常:当 hasNext() 返回 false 时,继续调用 next(),会抛出 NoSuchElementException(无此元素异常),切记“先判断、再取值”。
  2. 指针不复位:迭代器遍历完毕后,指针会停留在集合末尾,不会自动回到 0 索引;如果想再次遍历集合,必须重新调用 iterator() 方法,获取一个新的迭代器对象。
  3. next() 调用规范:一次循环中建议只调用一次 next() 方法,保证 hasNext() 与 next() 一一对应,避免出现指针错乱、元素漏取或重复取的问题。
  4. 并发修改异常:迭代器遍历集合期间,不能使用集合自身的 add()、remove() 方法 操作元素(比如 list.add()、list.remove()),否则会抛出 ConcurrentModificationException;若确实需要删除元素,只能使用迭代器自身的 remove() 方法;若需要添加元素,普通迭代器无法实现(需用列表迭代器)。

迭代器遍历代码示例(完整可运行)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        // 1. 获取迭代器对象
        Iterator<String> it = list.iterator();

        // 2. 遍历:先判断,再取值
        while (it.hasNext()) {
            // 3. 获取当前元素,指针自动后移
            String str = it.next();
            System.out.println(str);

            // 可选:删除上一次next()返回的元素(此处演示删除"bbb")
            if ("bbb".equals(str)) {
                it.remove(); // 仅迭代器remove可用,不会抛并发修改异常
            }
        }

        // 遍历完毕,指针不复位,重新遍历需重新获取迭代器
        Iterator<String> it2 = list.iterator();
        while (it2.hasNext()) {
            System.out.println(it2.next()); // 输出:aaa、ccc
        }
    }
}

二、列表迭代器遍历(ListIterator 接口)

列表迭代器是 List 集合专属 的遍历工具,继承自 Iterator 接口,完全实现了 Iterator 的所有功能(hasNext()、next()、remove()),同时新增了“向前遍历、遍历中添加元素”的功能,完美解决了普通迭代器不能添加元素的痛点。

列表迭代器常用成员方法(新增+继承)

方法名说明
boolean hasNext()(继承)判断当前位置的下一个位置是否有元素,有为 true,无为 false
E next()(继承)获取当前位置元素,并将迭代器指针移向后一个位置
void remove()(继承)删除上一次 next() 或 previous() 方法返回的元素
boolean hasPrevious()(新增)判断当前位置的前一个位置是否有元素,有为 true,无为 false(支持向前遍历)
E previous()(新增)获取当前位置前一个元素,并将迭代器指针向前移一个位置(向前遍历核心)
public void add(E e)(新增)将元素插入当前指针位置,并将迭代器对象移向后一个位置(遍历中添加元素)

列表迭代器核心优势

列表迭代器代码示例(遍历中添加元素)

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ListIteratorDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        // 1. 获取List集合专属的列表迭代器
        ListIterator<String> it2 = list.listIterator();

        // 2. 向后遍历,遍历中添加元素
        while (it2.hasNext()) {
            String str2 = it2.next();
            System.out.println(str2);
            // 当遍历到"bbb"时,在其后面添加"qqq"
            if ("bbb".equals(str2)) {
                it2.add("qqq"); // 遍历中添加,无异常
            }
        }
        System.out.println("添加元素后:" + list); // 输出:[aaa, bbb, qqq, ccc]

        // 3. 向前遍历(演示双向遍历)
        System.out.println("向前遍历:");
        while (it2.hasPrevious()) {
            String str3 = it2.previous();
            System.out.println(str3); // 输出:ccc、qqq、bbb、aaa
        }
    }
}

三、增强 for 遍历

增强 for 循环是 JDK 5 以后引入的语法糖,底层本质就是一个 Iterator 迭代器,目的是简化迭代器的代码书写,让遍历变得更简洁、更易读,无需手动获取迭代器、判断 hasNext()、调用 next()。

增强 for 核心特点

  1. 语法极简,仅用于“遍历”,不能用于遍历中添加、删除元素(否则抛并发修改异常)。
  2. 适配范围:所有单列集合(Collection 及其子类)和数组,双列集合(Map)不能直接使用。
  3. 遍历过程中,不能操作集合的索引(无索引相关方法)。

增强 for 标准格式

for(元素的数据类型 变量名 : 数组或者单列集合) {
    // 循环体:变量名 依次表示集合/数组中的每一个元素
}

增强 for 代码示例(集合+数组)

import java.util.ArrayList;
import java.util.List;

public class ForEachDemo {
    public static void main(String[] args) {
        // 1. 增强 for 遍历 List 集合
        List<String> list = new ArrayList<>();
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");

        for (String s : list) {
            System.out.println(s); // s 依次表示集合中的每一个元素
        }

        // 2. 增强 for 遍历数组
        Integer[] arr = {1, 2, 3, 4, 5};
        for (Integer num : arr) {
            System.out.println(num); // num 依次表示数组中的每一个元素
        }

        // 重要细节:修改第三方变量s,不会改变集合本身的值
        for (String s : list) {
            s = "zhaoliu"; // 仅修改变量s的值,集合中的元素不变
        }
        System.out.println("修改后集合:" + list); // 输出:[zhangsan, lisi, wangwu]
    }
}

实用小技巧

增强 for 快速生成:在 IDEA 中,输入“集合/数组名 + for”,按下回车键,即可自动生成增强 for 循环代码,高效又不易出错。

遍历方式大汇总(实用度拉满)

结合前面所学,我们整理出 Collection 和 List 集合的所有遍历方式,明确每种方式的适用场景,避免盲目使用。

一、Collection 集合的三种遍历方式(通用)

遍历方式核心适用场景优势不足
迭代器(Iterator)遍历中需要删除元素通用、可删除元素语法相对繁琐
增强 for 循环仅需要遍历元素,无需修改语法极简、易读不能增删元素、无索引
Lambda 表达式遍历仅需要遍历元素,追求代码简洁最简洁、代码量最少不能增删元素、无索引

简单总结:

  1. 遍历中需要删除元素 → 用 迭代器
  2. 仅仅想遍历,追求简洁 → 用 Lambda 或 增强 for

二、List 集合的五种遍历方式(最全)

List 集合因为有索引,除了继承 Collection 的三种遍历方式,还新增了“普通 for 循环”和“列表迭代器”两种方式,共五种,覆盖所有场景:

遍历方式核心适用场景优势
迭代器(Iterator)遍历中需要删除元素通用、可删除
列表迭代器(ListIterator)遍历中需要添加/删除元素、双向遍历可增删、可双向遍历
增强 for 循环仅遍历,无需修改简洁易读
Lambda 表达式遍历仅遍历,追求极简代码量最少
普通 for 循环遍历中需要操作索引(如修改指定位置元素)可操作索引、灵活

简单总结:

  1. 遍历中增删 → 列表迭代器
  2. 遍历中删 → 迭代器
  3. 操作索引 → 普通 for
  4. 仅遍历 → Lambda / 增强 for

全套遍历方式代码示例(一次性掌握)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class AllTraversalDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        // 1. 迭代器遍历(可删除)
        System.out.println("=== 迭代器遍历 ===");
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }

        // 2. 列表迭代器遍历(可增删、双向)
        System.out.println("=== 列表迭代器遍历 ===");
        ListIterator<String> it2 = list.listIterator();
        while (it2.hasNext()) {
            String str2 = it2.next();
            if ("bbb".equals(str2)) {
                it2.add("qqq"); // 遍历中添加
            }
            System.out.println(str2);
        }

        // 3. 增强 for 遍历(仅遍历)
        System.out.println("=== 增强 for 遍历 ===");
        for (String s : list) {
            System.out.println(s);
        }

        // 4. 普通 for 循环遍历(操作索引)
        System.out.println("=== 普通 for 遍历 ===");
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
            // 可操作索引:修改指定位置元素
            if (i == 0) {
                list.set(i, "aaa111");
            }
        }

        // 5. Lambda 表达式遍历(极简)
        System.out.println("=== Lambda 遍历 ===");
        list.forEach(s -> System.out.println(s));
    }
}

Iterator 底层源码解析(面试高频)

很多同学在遍历集合时会遇到并发修改异常,却不知道根源在哪里;面试中,面试官也常问“迭代器底层原理”,看懂这段源码,就能彻底搞懂所有问题(以 ArrayList 中的迭代器为例):

// ArrayList 中获取迭代器的方法
public Iterator<E> iterator() {
    return new Itr(); // 每次调用iterator(),都会创建一个新的Itr对象(内部类)
}

// 迭代器的底层实现:ArrayList的内部类Itr,实现了Iterator接口
private class Itr implements Iterator<E> {
    int cursor;      // 迭代器指针(光标),默认指向集合0索引
    int lastRet = -1; // 记录上一次调用next()返回的元素索引,初始值-1(表示未操作)
    int expectedModCount = modCount; // 记录集合的修改次数(创建迭代器时的初始次数)

    // hasNext()原理:判断指针是否到达集合末尾(cursor等于集合大小,说明无元素)
    public boolean hasNext() {
        return cursor != size;
    }

    // next()原理:获取当前指针元素,指针后移,并校验集合是否被修改
    public E next() {
        checkForComodification(); // 核心校验:判断集合是否被非法修改
        int i = cursor; // 记录当前指针位置
        if (i >= size)
            throw new NoSuchElementException(); // 指针越界,抛异常
        Object[] elementData = ArrayList.this.elementData; // 获取ArrayList底层数组
        if (i >= elementData.length)
            throw new ConcurrentModificationException(); // 并发修改异常
        cursor = i + 1; // 指针后移一位
        return (E) elementData[lastRet = i]; // 返回当前元素,并更新lastRet
    }

    // 校验集合是否被修改:expectedModCount(迭代器记录的次数) != modCount(集合实际次数)
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

源码核心解读(一句话搞懂)

  1. cursor:迭代器的指针,控制遍历的位置,默认0,每次next()后+1。
  2. modCount:ArrayList 中的成员变量,记录集合的修改次数(每调用一次 add()、remove(),modCount 就自增1)。
  3. expectedModCount:迭代器创建时,复制一份当前集合的 modCount,作为“预期修改次数”。
  4. 并发修改异常根源:遍历期间,若用集合的 add()、remove() 方法修改元素,会导致 modCount 自增,此时 modCount != expectedModCount,调用 next() 时,checkForComodification() 校验失败,抛出异常。

避坑关键

遍历期间,只要不修改 modCountexpectedModCount 的差值,就不会抛异常:

全篇核心干货总结(速记版)

  1. 迭代器(Iterator):通用遍历,可删不可加,指针不复位,遍历中不能用集合方法修改元素。
  2. 列表迭代器(ListIterator):List 专属,可增可删、双向遍历,解决普通迭代器不能添加的痛点。
  3. 增强 for:底层是迭代器,语法极简,仅用于遍历,不能增删元素,修改第三方变量不影响集合。
  4. 遍历方式选择:
    • Collection:迭代器(删)、增强 for/Lambda(仅遍历)
    • List:新增普通 for(操作索引)、列表迭代器(增删)
  5. 并发修改异常:根源是 modCount != expectedModCount,遍历中用迭代器修改元素可避免。
  6. 面试重点:Iterator 底层源码(cursor、modCount、expectedModCount)、列表迭代器新增功能。

写在最后

集合遍历是 Java 基础中最常用、最易出错的知识点,无论是日常业务开发、刷算法题,还是面试,都会高频遇到。

很多初学者容易在“遍历中增删元素”“指针复位”“并发修改异常”这几个点上踩坑,其实只要吃透本文的知识点,多敲几遍代码,理解底层原理,就能轻松规避所有问题。

建议大家把文中的代码亲手敲一遍,重点练习“迭代器删除”“列表迭代器添加”“并发修改异常规避”这三个场景,加深记忆。

以上就是Java集合遍历全指南(迭代器&amp;增强for及列表迭代器)的详细内容,更多关于Java集合遍历指南的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文