不能在循环里连续调用 next() 两次,因为 hasNext() 仅判断存在性而不移动指针,而 next() 每次都返回当前元素并推进游标;连续调用会导致跳过元素或抛 NoSuchElementException。

为什么不能在循环里连续调用 next() 两次
直接在 while 循环体里写两次 iterator.next(),会导致跳过元素甚至抛出 NoSuchElementException。因为 hasNext() 只判断“下一个是否存在”,不移动指针;而 next() 既返回当前元素,又把内部游标推进到下一个位置。
常见错误写法:
while (iterator.hasNext()) {
System.out.println(iterator.next()); // 第一次取
System.out.println(iterator.next()); // 第二次取 —— 这里可能越界或跳元素
}
- 如果集合只有 1 个元素,第二次
next()必然抛异常 - 即使有多个元素,也会每轮跳过 1 个(比如遍历 [a,b,c] 输出 a、c,漏掉 b)
hasNext() 和 next() 必须严格配对使用
标准模式是:每次确认有下一项,就只调用一次 next() 取值。这是 Iterator 协议的核心约定,不是风格偏好。
正确写法(推荐):
立即学习“Java免费学习笔记(深入)”;
while (iterator.hasNext()) {
String item = iterator.next(); // 一次判断,一次取值
System.out.println(item);
}
- 适用于
ArrayList、LinkedList、HashSet等所有实现了Iterable的传统集合 - 不要把
next()拆到 if 或其他分支里重复调用——只要调用,就必须确保前面刚通过hasNext()校验过 - 如果需要“预读”下一个元素(比如做相邻比较),得自己缓存,不能依赖多次
next()
用 for-each 替代手写 Iterator 更安全,但失去 remove 能力
绝大多数场景下,直接用 for (Type e : collection) 更简洁且不会出错,它底层就是帮你封装了 hasNext()/next() 配对逻辑。
但注意:
- 如果要在遍历中删除元素,
for-each会抛ConcurrentModificationException,必须退回用显式Iterator并调用iterator.remove() -
for-each对null元素不敏感,而手写 Iterator 在next()返回null时需自行判空(尤其对Map.values()等可能含null的集合)
多线程环境下 hasNext() 返回 true 不代表 next() 一定成功
传统集合(如 ArrayList)不是线程安全的。即使 hasNext() 刚返回 true,另一线程立刻删掉最后一个元素,紧接着本线程调用 next() 就会抛 NoSuchElementException。
- 这不是 API 设计缺陷,而是迭代器快照语义的自然结果
- 解决方法不是加锁整个循环,而是改用线程安全集合(如
CopyOnWriteArrayList)或同步块包裹单次hasNext()+next()组合 - 别试图靠重试
hasNext()来规避——竞态条件仍存在,且可能陷入死循环
hasNext() 的瞬时性”和“next() 的副作用性”。这两点一旦混淆,bug 往往只在高并发或边界数据下暴露。


















