首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【集合框架迭代器底层解析】

【集合框架迭代器底层解析】

作者头像
艾伦耶格尔
发布2025-08-28 15:52:38
发布2025-08-28 15:52:38
12200
代码可运行
举报
文章被收录于专栏:Java基础Java基础
运行总次数:0
代码可运行

“遍历集合不用 Iterator,就像开车不用方向盘。” Iterator 是 Java 集合框架中最基础、最常用的接口之一。 你可能每天都在用 for-each 循环遍历 ArrayListHashSet,但你是否想过: 它是如何工作的?为什么删除元素时必须用 iterator.remove()ConcurrentModificationException 又是怎么产生的?

本文将带你深入 JDK 源码,从底层实现、设计模式到线程安全,全面剖析 Iterator 的工作原理。


📌 本文导读

  • ✅ Iterator 接口定义与基本使用
  • ✅ ArrayList 中的 Iterator 实现源码解析
  • ✅ fail-fast 机制:快速失败是如何实现的?
  • ✅ remove() 方法的线程安全性问题
  • ✅ ListIterator 与双向遍历
  • ✅ foreach 循环与 Iterator 的关系
  • ✅ 总结与最佳实践

一、Iterator 接口定义

Iteratorjava.util 包中的一个核心接口,定义了集合遍历的基本行为:

代码语言:javascript
代码运行次数:0
运行
复制
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();        // 可选操作
    default void forEachRemaining(Consumer<? super E> action);
}

方法

说明

hasNext()

是否还有下一个元素

next()

返回下一个元素,指针后移

remove()

删除当前元素(调用 next() 后才能调用)

forEachRemaining()

JDK 8+,对剩余元素执行操作

💡 注意:remove()可选操作,某些集合(如 Arrays.asList() 返回的列表)不支持删除,调用会抛出 UnsupportedOperationException


二、ArrayList 的 Iterator 实现源码解析

我们以最常用的 ArrayList 为例,深入其内部实现。

1. 获取 Iterator
代码语言:javascript
代码运行次数:0
运行
复制
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

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

我们进入 ArrayList.iterator() 源码(JDK 17):

代码语言:javascript
代码运行次数:0
运行
复制
public Iterator<E> iterator() {
    return new Itr();
}

它返回了一个内部类 Itr 的实例。


2. Itr 内部类源码核心字段
代码语言:javascript
代码运行次数:0
运行
复制
private class Itr implements Iterator<E> {
    int cursor;        // 下一个要返回的元素索引
    int lastRet = -1;  // 最近一次返回的元素索引
    int expectedModCount = modCount; // 期望的修改次数

    // ...
}

字段

说明

cursor

游标,指向下一个要读取的元素

lastRet

上一次 next() 返回的元素索引

expectedModCount

关键!用于 fail-fast 检查

🔑 modCountArrayList 父类 AbstractList 中的字段,记录集合被结构性修改的次数(如 addremove)。


3. hasNext() 与 next() 方法
代码语言:javascript
代码运行次数:0
运行
复制
public boolean hasNext() {
    return cursor != size;
}

public E next() {
    checkForComodification(); // 检查并发修改
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    lastRet = i;
    return (E) elementData[lastRet];
}
关键点解析:
  • checkForComodification():检查 modCount 是否等于 expectedModCount
  • 如果不等,说明集合被外部修改过,抛出 ConcurrentModificationException
  • cursor 指针移动,返回元素

4. remove() 方法
代码语言:javascript
代码运行次数:0
运行
复制
public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet); // 调用 ArrayList 的 remove
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount; // 同步修改次数
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}
为什么必须用 iterator.remove()
  • 因为它会同步更新 expectedModCount,避免下次 next() 时触发 ConcurrentModificationException
  • 如果你用 list.remove()modCount 会变,但 expectedModCount 不变,下一次 next() 就会抛异常

三、fail-fast 机制:快速失败

什么是 fail-fast?

“一旦发现集合在迭代过程中被外部修改,立即抛出异常,而不是等到不确定的未来。”

这是 Java 集合框架的一种安全保护机制

如何实现?
代码语言:javascript
代码运行次数:0
运行
复制
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
  • expectedModCount 在 Iterator 创建时被初始化为当前 modCount
  • 每次 addremove 操作,modCount++
  • Iterator 每次操作前检查两者是否相等

⚠️ 注意:fail-fast 不是线程安全的保证!它只能检测到单线程下的意外修改,多线程环境下依然可能漏判。


四、ListIterator:双向迭代

ListIterator 继承自 Iterator,支持双向遍历元素替换

代码语言:javascript
代码运行次数:0
运行
复制
public interface ListIterator<E> extends Iterator<E> {
    boolean hasPrevious();
    E previous();
    int nextIndex();
    int previousIndex();
    void set(E e);
    void add(E e);
}
示例:逆序遍历
代码语言:javascript
代码运行次数:0
运行
复制
ListIterator<String> lit = list.listIterator(list.size());
while (lit.hasPrevious()) {
    System.out.println(lit.previous());
}

ArrayList 中的 ListItr 实现与 Itr 类似,但维护了双向指针。


五、foreach 循环与 Iterator 的关系

你写的:

代码语言:javascript
代码运行次数:0
运行
复制
for (String s : list) {
    System.out.println(s);
}

在编译后,会被自动转换为 Iterator 形式

代码语言:javascript
代码运行次数:0
运行
复制
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
    String s = it.next();
    System.out.println(s);
}

✅ 所以:foreach 循环的本质就是 Iterator


六、线程安全问题与替代方案

❌ 问题:Iterator 不是线程安全的
代码语言:javascript
代码运行次数:0
运行
复制
// 线程1
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next(); // 可能抛 ConcurrentModificationException
}

// 线程2
list.add("D"); // 外部修改
✅ 解决方案

场景

推荐方案

单线程遍历删除

使用 iterator.remove()

多线程环境

使用 CopyOnWriteArrayList

高并发读写

使用 ConcurrentHashMap、BlockingQueue 等并发集合

CopyOnWriteArrayListIteratorfail-safe 的,基于快照遍历,不会抛 ConcurrentModificationException


🎯 总结:Iterator 核心要点

机制

原理

注意事项

迭代指针

cursor 指向下个元素

hasNext() 判断是否越界

fail-fast

modCount vs expectedModCount

检测意外修改

remove() 安全性

同步更新 expectedModCount

必须先 next() 才能 remove

foreach 本质

编译为 Iterator

不能在循环中修改集合

线程安全

非线程安全

多线程用并发集合


✅ 最佳实践

  1. 遍历中删除元素,务必使用 iterator.remove()
  2. 避免在 foreach 循环中调用 list.remove()
  3. 多线程环境下,使用 Concurrent 包下的集合
  4. 理解 fail-fast 是“检测”而非“防止”并发修改

🔚 写在最后

Iterator 虽然只是一个简单的接口,但其背后蕴含着 设计模式(迭代器模式)、安全机制(fail-fast)、线程模型 等深刻的设计思想。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📌 本文导读
  • 一、Iterator 接口定义
  • 二、ArrayList 的 Iterator 实现源码解析
    • 1. 获取 Iterator
    • 2. Itr 内部类源码核心字段
    • 3. hasNext() 与 next() 方法
      • 关键点解析:
    • 4. remove() 方法
      • 为什么必须用 iterator.remove()?
  • 三、fail-fast 机制:快速失败
    • 什么是 fail-fast?
    • 如何实现?
  • 四、ListIterator:双向迭代
    • 示例:逆序遍历
  • 五、foreach 循环与 Iterator 的关系
  • 六、线程安全问题与替代方案
    • ❌ 问题:Iterator 不是线程安全的
    • ✅ 解决方案
  • 🎯 总结:Iterator 核心要点
  • ✅ 最佳实践
  • 🔚 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档