Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

    • Java集合概述
    • ArrayList
    • LinkedList
    • HashMap
    • LinkedHashMap
    • HashSet
    • TreeMap
    • Queue&Deque
    • 迭代器与遍历机制
      • 1. Iterable
      • 2. Iterator
      • 3. 增强 for 的本质
      • 4. 结构性修改
      • 5. fail-fast 机制
      • 6. 遍历时删除元素
      • 7. ListIterator
      • 8. Map 的遍历
      • 9. Stream 遍历
      • 10. 并行遍历
      • 11. fail-safe 迭代
      • 12. 遍历性能建议
      • Tips 快问快答
    • Collections工具类
    • 集合排序与比较器
    • 集合选型与常见问题
  • Java并发

  • Java IO

  • JVM

目录

迭代器与遍历机制

遍历是集合最常见的操作之一,但它背后包含 Iterable、Iterator、增强 for、fail-fast、结构性修改、删除元素等机制。很多 ConcurrentModificationException 都来自对遍历机制理解不清。

# 1. Iterable

Iterable 表示一个对象可以被迭代。

public interface Iterable<T> {
    Iterator<T> iterator();
}

只要实现了 Iterable,就可以使用增强 for:

for (String name : names) {
    System.out.println(name);
}

集合遍历模型:

Collection 实现 Iterable
        │
        ▼
iterator()
        │
        ▼
Iterator
        │
        ├─ hasNext()
        ├─ next()
        └─ remove()

# 2. Iterator

Iterator 用于逐个访问集合元素。

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    System.out.println(name);
}

核心方法:

方法 作用
hasNext() 是否还有下一个元素
next() 返回下一个元素
remove() 删除上一次 next() 返回的元素

remove() 只能在 next() 之后调用,否则会抛 IllegalStateException。

# 3. 增强 for 的本质

增强 for:

for (String name : names) {
    System.out.println(name);
}

大致等价于:

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    System.out.println(name);
}

因此增强 for 遍历集合时,本质仍然使用迭代器。

数组的增强 for 不走 Iterator,而是按下标遍历。

# 4. 结构性修改

结构性修改指会改变集合大小或内部结构的操作。

常见结构性修改:

  • add
  • remove
  • clear
  • put
  • resize

非结构性修改:

  • ArrayList.set(index, value)
  • Map.Entry.setValue

结构性修改会影响迭代器的预期状态。

# 5. fail-fast 机制

很多集合维护一个修改次数 modCount。迭代器创建时记录 expectedModCount。

集合 modCount = 3
   │ 创建迭代器
   ▼
迭代器 expectedModCount = 3

遍历中集合 add/remove
   │
   ▼
集合 modCount = 4

迭代器 next 时检查:
modCount != expectedModCount
   │
   ▼
ConcurrentModificationException

示例:

for (String name : names) {
    if (name.startsWith("A")) {
        names.remove(name);
    }
}

这可能抛出 ConcurrentModificationException。

注意:fail-fast 是尽力而为的错误检测,不是并发安全保证。

# 6. 遍历时删除元素

正确方式一:使用迭代器删除。

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.startsWith("A")) {
        iterator.remove();
    }
}

正确方式二:使用 removeIf。

names.removeIf(name -> name.startsWith("A"));

正确方式三:创建新集合。

List<String> result = names.stream()
        .filter(name -> !name.startsWith("A"))
        .toList();

如果原集合需要保留,创建新集合更安全。

# 7. ListIterator

ListIterator 是 List 专用迭代器,支持双向遍历和修改。

ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if ("A".equals(name)) {
        iterator.set("AA");
    }
}

方法:

方法 作用
hasPrevious() 是否有前一个元素
previous() 返回前一个元素
nextIndex() 下一个元素索引
previousIndex() 前一个元素索引
set(e) 替换上一次访问的元素
add(e) 在当前位置添加元素

ListIterator 适合需要边遍历边局部修改 List 的场景。

# 8. Map 的遍历

推荐遍历 entrySet:

for (Map.Entry<Long, User> entry : userMap.entrySet()) {
    Long id = entry.getKey();
    User user = entry.getValue();
}

只遍历 key:

for (Long id : userMap.keySet()) {
}

只遍历 value:

for (User user : userMap.values()) {
}

避免:

for (Long id : userMap.keySet()) {
    User user = userMap.get(id);
}

这会多一次 hash 查找。

# 9. Stream 遍历

Stream 更适合表达数据处理流水线:

List<String> activeNames = users.stream()
        .filter(User::isActive)
        .map(User::getName)
        .toList();

处理流程:

source
  │
  ▼
filter
  │
  ▼
map
  │
  ▼
collect / toList

Stream 不应被滥用在有大量副作用的逻辑里:

users.stream().forEach(user -> externalService.call(user));

如果主要是执行动作而不是转换数据,普通循环更清晰。

# 10. 并行遍历

parallelStream() 会把任务提交到公共 ForkJoinPool。

users.parallelStream()
        .map(this::calculate)
        .toList();

适合:

  • CPU 密集型。
  • 数据量足够大。
  • 单个元素处理相对独立。
  • 没有共享可变状态。

不适合:

  • IO 密集且阻塞不可控。
  • 数据量很小。
  • 需要保持严格顺序。
  • 依赖 ThreadLocal 上下文。
  • 会修改共享集合。

并行流不是“加速开关”,使用前要压测。

# 11. fail-safe 迭代

某些并发集合使用弱一致性迭代器,不会在并发修改时抛 fail-fast。

例如:

  • ConcurrentHashMap
  • CopyOnWriteArrayList

CopyOnWriteArrayList 的迭代器基于快照:

创建迭代器
  │
  ▼
持有当前数组快照
  │
  ▼
后续写操作复制新数组
  │
  ▼
迭代器仍遍历旧快照

这适合读多写少场景,但写入成本高。

# 12. 遍历性能建议

场景 建议
ArrayList 普通遍历 增强 for 或普通 for 都可以
LinkedList 遍历 使用增强 for 或 iterator,避免循环 get(i)
Map 同时要 key/value 遍历 entrySet
遍历中删除 使用 iterator.remove 或 removeIf
数据转换 使用 Stream
高性能热点路径 写清晰循环并压测

# Tips 快问快答

Q:增强 for 的底层是什么? A:遍历集合时底层是 Iterator,遍历数组时是下标循环。

Q:为什么遍历时直接 remove 会报错? A:集合的 modCount 和迭代器的 expectedModCount 不一致,触发 fail-fast。

Q:遍历时删除元素的推荐方式是什么? A:使用 Iterator.remove() 或 removeIf()。

Q:fail-fast 是线程安全机制吗? A:不是,它只是尽力发现错误修改。

Q:ListIterator 比 Iterator 多什么? A:支持双向遍历、设置元素和在当前位置添加元素。

Q:Map 遍历为什么推荐 entrySet? A:同时拿 key 和 value,避免二次查找。

Q:parallelStream 一定更快吗? A:不一定。它有调度开销,还可能受共享状态和阻塞影响。

Q:CopyOnWriteArrayList 遍历为什么不会报并发修改? A:迭代器遍历的是创建时的数组快照。

Q:Stream 适合替代所有循环吗? A:不适合。复杂副作用逻辑用普通循环更清楚。

Q:LinkedList 为什么不要用 for+i+get 遍历? A:每次 get(i) 都要遍历链表,整体可能退化为 O(n²)。

上次更新: 2026/06/24, 16:37:44
Queue&Deque
Collections工具类

← Queue&Deque Collections工具类→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式