集合的迭代器遍历复习

迭代器 Iterator 与增强 for 循环(改写与补充)

一、迭代器 Iterator

1. 作用

  • 统一遍历各种集合(Collection 下的 ListSet 等),而不暴露集合内部结构。
  • 提供单向、只读(可有限删除)的访问方式。

2. 常用方法

方法说明
boolean hasNext()判断迭代器中是否还有下一个元素(即是否可以安全调用 next()
E next()返回下一个元素,并将迭代器位置向后移动一位(必须先用 hasNext() 检查
void remove()删除 next() 方法最后一次返回的元素(可选操作,部分迭代器不支持会抛出异常)

注意

  • ListSet:✅ 都可以直接使用 Iterator 和增强 for 循环(因为它们都实现了 Collection 接口,Collection 继承了 Iterable)。
  • Map:❌ 不能直接使用,但可以通过 keySet()values()entrySet() 返回的 Collection 视图来间接使用。

ListSet(直接使用)

java
复制代码
List<String> list = new ArrayList<>(); Set<String> set = new HashSet<>(); // 迭代器 Iterator<String> it1 = list.iterator(); Iterator<String> it2 = set.iterator(); // 增强 for for (String s : list) { } for (String s : set) { }

Map(间接使用)

java
复制代码
Map<String, Integer> map = new HashMap<>(); // 遍历键 for (String key : map.keySet()) { } // 遍历值 for (Integer val : map.values()) { } // 遍历键值对 for (Map.Entry<String, Integer> entry : map.entrySet()) { } // 使用迭代器(同理) Iterator<String> keyIterator = map.keySet().iterator(); Iterator<Map.Entry<String, Integer>> entryIterator = map.entrySet().iterator();

为什么 Map 不能直接使用?

  • Map 接口没有继承 CollectionIterable,它属于独立的集合层次结构。
  • 设计上,Map 存储的是键值对,遍历方式有三种(键、值、条目),无法统一为单个元素类型。因此需要开发者明确选择视图。
集合类型直接使用 Iterator直接使用增强 for
List
Set
Map❌(需通过 keySet()/entrySet() 等)❌(同上)

3. 使用细节(重要)

✅ 正确用法
java
复制代码
Iterator<String> it = list.iterator(); while (it.hasNext()) { String s = it.next(); // 处理 s }
❌ 常见错误 1:遍历完成后继续使用 next()
java
复制代码
while (it.hasNext()) { System.out.println(it.next()); } it.next(); // 抛出 NoSuchElementException,因为已经没有下一个元素
❌ 常见错误 2:遍历过程中直接使用集合对象的增删方法
java
复制代码
while (it.hasNext()) { String s = it.next(); if (s.equals("xxx")) { list.remove(s); // 抛出 ConcurrentModificationException(快速失败机制) } }
  • 原因:迭代器内部维护了集合的修改次数计数,若集合结构被其他方式修改(非迭代器自身的 remove),迭代器会立即抛出异常,防止数据不一致。
✅ 正确删除元素:使用迭代器的 remove()
java
复制代码
while (it.hasNext()) { String s = it.next(); if (s.equals("xxx")) { it.remove(); // 删除当前元素,不会破坏迭代状态 } }
⚠️ 增加元素
  • 迭代器的 remove() 只能删除,不能增加。若需要在遍历时增加元素:
    • 使用 ListIterator(专门针对 List 集合,提供 add() 方法)。
    • 或者先记录要增加的元素,遍历结束后再统一添加。

4. 扩展内容

4.1 ListIterator(子接口)
  • 只适用于 List 集合,可以双向遍历hasPrevious()previous())。
  • 支持添加元素 add()
  • 支持修改当前元素 set()
4.2 快速失败机制(fail-fast)
  • ArrayListHashSet 等大多数集合的迭代器都是 fail-fast 的。
  • 如果在迭代过程中直接修改集合(非迭代器方法),会抛出 ConcurrentModificationException
  • 并发集合(ConcurrentHashMapCopyOnWriteArrayList)采用 fail-safe 机制,不会抛出该异常,但可能读到旧数据。

5. 迭代器的局限性

  • 单向:只能从前向后遍历(ListIterator 除外)。
  • 只能删除,不能修改元素的值(修改值可以通过 next() 拿到引用后直接修改对象内部状态,但不能替换元素本身)。
  • 不适用于 MapMap 本身不是 Collection,需要先获取 keySet()values()entrySet() 的迭代器。

二、增强 for 循环(for-each)

1. 含义

  • 语法糖,底层编译后仍是普通 for 循环或迭代器(数组使用普通 for,集合使用 Iterator)。
  • 目的:简化遍历代码,避免显式操作迭代器。

2. 语法

java
复制代码
for (元素类型 变量名 : 数组或 Iterable 对象) { // 循环体 }

3. 示例

java
复制代码
// 遍历数组 int[] arr = {1,2,3}; for (int num : arr) { System.out.println(num); } // 遍历集合 List<String> list = Arrays.asList("A","B","C"); for (String s : list) { System.out.println(s); }

4. 本质(反编译后)

  • 数组 → 普通 for 循环(索引遍历)

  • Iterable 实现类(包括所有 Collection → 编译为:

    java
    复制代码
    for (Iterator it = collection.iterator(); it.hasNext(); ) { String s = (String) it.next(); // ... }

5. 使用限制(与迭代器相同)

❌ 不能在增强 for 循环中直接修改集合结构(增删)
java
复制代码
for (String s : list) { if (s.equals("B")) { list.remove(s); // 抛出 ConcurrentModificationException } }
✅ 若需删除,仍要使用原始迭代器,或者改用普通 for 循环倒序删除(针对 List)。
java
复制代码
// 正确删除方式之一:普通 for 倒序 for (int i = list.size()-1; i >= 0; i--) { if (list.get(i).equals("B")) { list.remove(i); } }
⚠️ 增强 for 循环无法获取当前元素的索引(不像普通 for 有下标变量)。
⚠️ 无法修改迭代变量(变量是局部副本,重新赋值不影响集合中的元素)。
java
复制代码
for (String s : list) { s = "new"; // 仅改变局部变量 s,不修改原集合元素 }

6. Java 8 之后:forEach() 方法

  • 所有 Iterable 接口(包括 Collection)都提供了 forEach(Consumer) 方法。

  • 结合 Lambda 表达式,更简洁:

    java
    复制代码
    list.forEach(s -> System.out.println(s)); // 或使用方法引用 list.forEach(System.out::println);
  • 同样受快速失败机制约束,不能在遍历中增删(除非使用线程安全的集合)。


三、总结建议

场景推荐方式
单纯遍历元素,不修改集合增强 for 循环 或 forEach()
需要删除元素显式使用 Iterator.remove()
需要双向遍历 / 添加元素(仅 List)使用 ListIterator
需要索引普通 for 循环(通过 get(i)
需要修改元素的值增强 for 循环拿到引用后修改(对象内部) 或 普通 for 循环替换整个元素
高并发环境下遍历使用并发集合(CopyOnWriteArrayList 等)或手动加锁

核心原则:在迭代过程中,除非使用迭代器自身提供的修改方法(如 remove),否则不要通过集合对象直接增删元素,以避免 ConcurrentModificationException

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
凌空零
作者分享
口述学习内容 5.8 事务隔离级别:为解决多个事务并行操作数据库可能出现脏读、不可重复读与幻读这三种问题,得出了四种事务级别,作为不同场景下的事务问题解决方案。 读未提交;问题全有。 脏读:读到了别人已修改但还没提交的数据,后面别人的事务可能回滚,读到的就成了假数据。 不可重复读:同一事务内,两次读数据的值不同,因为期间被别人修改了。 幻读:同一事务内,两次查询数据的行数不同,因为期间数据被别人增加或删除了。 读已提交:通过锁表?将还没提交的行锁起来,解决脏读。 可重复读:通过给数据库加版本号,同一事务内读到的数据是同一个版本的数据库,解决了不可重复读。 串行:通过锁数据库?同一时间只有一个事务内操作数据库,解决了幻读。 5.7 mysql:DDL、DML。单表函数、多表函数:聚合函数。 项目:1.黑马苍穹外卖实现员工登录、新增员工功能。2.登录功能将明文登录密码转为密文存入数据库的功能。3.通过转换工具将员工DTO的属性拷贝到员工实体中,实现便捷编写代码。4.新增员工的用户名有唯一索引且与数据库表中已有数据重复而发现运行时异常报错,通过编写自定义用户已存在异常,编写全局异常处理器捕获自定义异常,实现异常处理。且统一异常处理,代码可复用、异常与业务代码分离,代码可读性高。
2
java高级与sping的学习
3
Spring 框架实现“统一逻辑处理”的两种核心方式
3
java集合复习
3
java线程入门复习
3
下载 APP