关键在于让指针仅在该动时按确定逻辑移动;必须严格遵循“先判后取”,即每次循环只调用一次next()且仅在hasNext()为true后执行,避免跳过元素、异常或死循环。

关键不是“怎么让指针动起来”,而是“让指针只在该动的时候动、按确定的逻辑动”。死循环在迭代器遍历中几乎总是因为 hasNext() 和 next() 的调用节奏错乱,或者 迭代器状态被外部干扰 导致的。
必须严格遵循“先判后取”流程
迭代器的指针移动完全由 next() 触发,而 hasNext() 只做判断、不移动指针。一旦跳过判断或重复调用 next(),就会跳过元素、抛异常,甚至陷入看似“永远有下一个”的假象。
- ✅ 正确写法:每次循环只调用一次 next(),且一定在 hasNext() 为 true 后执行
- ❌ 错误写法:把 next() 放进 while 条件里(如
while (it.next() != null)),会导致每轮调用两次 next(),一半元素被跳过,还可能因 null 判定失效引发 NullPointerException - ❌ 危险写法:在循环体内多次调用 next() 而不检查 hasNext(),比如
String a = it.next(); String b = it.next();—— 第二个 next() 很可能越界
自定义迭代器要封闭内部状态
如果集合结构复杂(比如树、图、嵌套列表),迭代器必须自己管理游标,不能依赖集合字段或共享变量。否则多线程或多次遍历会互相污染状态。
- 树形结构用 Stack
记录待访问节点,hasNext() 查栈是否为空,next() 弹栈并压入子节点 - 图结构必须用 Set
记录已访问节点,next() 中先检查是否已访问,再推进,否则环路会让 hasNext() 永远返回 true - 嵌套集合(如 List
- >)需两级索引:外层 listIndex + 内层 cursor,hasNext() 要跳过空子列表,避免停在 null 上卡住
遍历中修改集合必须走 remove(),且仅限一次
直接调用集合的 remove() 或 add() 会触发 fail-fast 机制,导致 ConcurrentModificationException;而迭代器自己的 remove() 是唯一安全出口——但它要求严格时序:
立即学习“Java免费学习笔记(深入)”;
- 必须在调用 next() 之后、且尚未再次调用 next() 前执行
- 连续两次 remove() 不跟 next() 会抛 IllegalStateException
- 若需边遍历边插入,不要在原集合操作,应收集待添加元素,遍历结束后统一 addAll()
空集合和 null 元素要提前兜底
迭代器本身不处理 null 集合,也不保证元素非 null。常见空指针陷阱不在迭代逻辑里,而在取值后直接使用。
- 获取迭代器前先判空:
if (list != null && !list.isEmpty()) { Iterator<string> it = list.iterator(); ... }</string> - next() 返回的元素可能是 null(尤其自定义集合允许存 null),打印或调用方法前加 null 检查
- 增强 for 循环底层也是迭代器,同样受上述规则约束,不能绕过安全逻辑


















