集合的迭代器遍历复习
迭代器 Iterator 与增强 for 循环(改写与补充)
一、迭代器 Iterator
1. 作用
- 统一遍历各种集合(
Collection下的List、Set等),而不暴露集合内部结构。 - 提供单向、只读(可有限删除)的访问方式。
2. 常用方法
| 方法 | 说明 |
|---|---|
boolean hasNext() | 判断迭代器中是否还有下一个元素(即是否可以安全调用 next()) |
E next() | 返回下一个元素,并将迭代器位置向后移动一位(必须先用 hasNext() 检查) |
void remove() | 删除 next() 方法最后一次返回的元素(可选操作,部分迭代器不支持会抛出异常) |
注意
List和Set:✅ 都可以直接使用Iterator和增强 for 循环(因为它们都实现了Collection接口,Collection继承了Iterable)。Map:❌ 不能直接使用,但可以通过keySet()、values()或entrySet()返回的Collection视图来间接使用。
List 和 Set(直接使用)
▼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接口没有继承Collection或Iterable,它属于独立的集合层次结构。- 设计上,
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)
ArrayList、HashSet等大多数集合的迭代器都是 fail-fast 的。- 如果在迭代过程中直接修改集合(非迭代器方法),会抛出
ConcurrentModificationException。 - 并发集合(
ConcurrentHashMap、CopyOnWriteArrayList)采用 fail-safe 机制,不会抛出该异常,但可能读到旧数据。
5. 迭代器的局限性
- 单向:只能从前向后遍历(
ListIterator除外)。 - 只能删除,不能修改元素的值(修改值可以通过
next()拿到引用后直接修改对象内部状态,但不能替换元素本身)。 - 不适用于 Map:
Map本身不是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个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
内容推荐
Day 103✅ 今天做了:复习了多用户通信系统⏰ 明天计划:学习Java反射
1
Day 68时间19:00~ 22:00(3h)✅ 今天做了:Component注解、Mybatis配置、使用⏰ 明天计划:Lombok、Mapper映射、动态SQL📚 今日感悟:自动配置类DataSourceAutoConfiguration ,会读取properties文件,通过注解:@EnableConfigurationProperties(DataSourceProperties.cl
2
Day 19✅ 今天做了:MCP⏰ 明天计划:AI智能体构建📚 今日感悟:今天MCP问题有点多有点杂,明天找时间再捋一下。继续加油
1
Day 25✅ 今天做了:1、扇贝英语单词打卡2、英语听说读写、听力练习3、微信阅读15分钟4、编程导航学习⏰ 明天计划:待定📚 今日感悟:Keep going!
2
Day 104✅ 今天做了:学习了Java反射及快速入门⏰ 明天计划:继续学习Java反射
1
作者分享
口述学习内容
5.8
事务隔离级别:为解决多个事务并行操作数据库可能出现脏读、不可重复读与幻读这三种问题,得出了四种事务级别,作为不同场景下的事务问题解决方案。
读未提交;问题全有。
脏读:读到了别人已修改但还没提交的数据,后面别人的事务可能回滚,读到的就成了假数据。
不可重复读:同一事务内,两次读数据的值不同,因为期间被别人修改了。
幻读:同一事务内,两次查询数据的行数不同,因为期间数据被别人增加或删除了。
读已提交:通过锁表?将还没提交的行锁起来,解决脏读。
可重复读:通过给数据库加版本号,同一事务内读到的数据是同一个版本的数据库,解决了不可重复读。
串行:通过锁数据库?同一时间只有一个事务内操作数据库,解决了幻读。
5.7
mysql:DDL、DML。单表函数、多表函数:聚合函数。
项目:1.黑马苍穹外卖实现员工登录、新增员工功能。2.登录功能将明文登录密码转为密文存入数据库的功能。3.通过转换工具将员工DTO的属性拷贝到员工实体中,实现便捷编写代码。4.新增员工的用户名有唯一索引且与数据库表中已有数据重复而发现运行时异常报错,通过编写自定义用户已存在异常,编写全局异常处理器捕获自定义异常,实现异常处理。且统一异常处理,代码可复用、异常与业务代码分离,代码可读性高。
2
java高级与sping的学习
3
Spring 框架实现“统一逻辑处理”的两种核心方式
3
java集合复习
3
java线程入门复习
3
