Java 实例 – 集合遍历(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
一、前言
在 Java 开发中,集合遍历是一个基础但至关重要的操作。无论是处理用户数据、解析配置文件,还是构建复杂的业务逻辑,开发者都需要频繁地与集合(如 ArrayList
、HashMap
等)进行交互。然而,如何高效、安全地遍历集合,却常常成为开发者容易忽视的细节。本文将通过 Java 实例 – 集合遍历 的角度,结合代码示例和实际场景,逐步解析不同遍历方法的原理、使用场景及潜在陷阱,帮助读者建立系统的认知框架。
二、基础概念回顾
1. 什么是集合遍历?
集合遍历的核心目标是 按顺序访问集合中的每一个元素。例如,遍历一个 ArrayList
获取所有用户信息,或遍历 HashMap
检索特定键值对。在 Java 中,遍历方式的选择直接影响代码的效率和安全性。
比喻:
可以把集合想象成一个超市的货架,每个元素是货架上的商品。遍历的过程就像用扫描仪逐个扫描商品,记录它们的信息。不同遍历方式就像不同的扫描工具——有的需要手动逐个扫描(如普通 for
循环),有的能自动识别商品并快速处理(如 Stream API
)。
2. 常见的集合类型
Java 中的集合框架(java.util
)提供了多种实现类,如:
- 列表(List):如
ArrayList
,元素有序且可重复。 - 集合(Set):如
HashSet
,元素无序且不可重复。 - 映射(Map):如
HashMap
,存储键值对。
这些类型的遍历逻辑和限制各有不同,需要根据具体需求选择方法。
三、经典遍历方法详解
1. 普通 for
循环
这是最基础的遍历方式,适用于 已知索引顺序的集合(如 List
)。
示例代码:遍历 ArrayList
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange"));
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
优点与缺点
- 优点:
- 直观易懂,适合对索引敏感的场景(如修改元素位置)。
- 性能较高,因直接通过索引访问。
- 缺点:
- 不适用于无索引的集合(如
Set
)。 - 直接修改集合会引发异常:例如遍历过程中删除元素,会导致
ConcurrentModificationException
。
- 不适用于无索引的集合(如
2. 增强型 for
循环(for-each)
Java 5 引入的语法糖,语法简洁,不依赖索引,但 无法直接修改元素或集合。
示例代码:遍历 ArrayList
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange"));
for (String fruit : fruits) {
System.out.println(fruit);
}
优点与缺点
- 优点:
- 代码简洁,适合只读操作。
- 支持所有实现了
Iterable
接口的集合(如List
、Set
)。
- 缺点:
- 无法在遍历时修改元素或集合(如删除元素)。
- 无法获取元素的索引位置。
3. 迭代器(Iterator)
迭代器是 安全遍历 的关键工具,尤其适用于需要 在遍历时修改集合 的场景。
示例代码:遍历并删除元素
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange"));
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if (fruit.equals("Banana")) {
iterator.remove(); // 安全删除元素
}
}
迭代器的核心方法
方法 | 功能描述 |
---|---|
hasNext() | 判断是否有下一个元素 |
next() | 获取下一个元素 |
remove() | 删除当前元素(需在 next() 后调用) |
优点与缺点
- 优点:
- 支持遍历过程中修改集合(如删除元素)。
- 兼容所有实现了
Iterable
的集合。
- 缺点:
- 语法较繁琐,需手动管理
iterator
对象。 - 不支持添加元素(某些集合如
ArrayList
的迭代器不允许add
)。
- 语法较繁琐,需手动管理
四、进阶方法:Java 8 的 Stream API
Java 8 引入的 Stream
API,通过 函数式编程 的方式简化了集合处理,尤其适合 批量操作 和 并行计算。
示例代码:筛选并打印元素
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
fruits.stream()
.filter(fruit -> fruit.startsWith("A")) // 筛选以"A"开头的元素
.forEach(System.out::println); // 打印结果
关键特性
- 惰性求值:只有在终端操作(如
forEach
)时才执行计算。 - 链式调用:支持
map
、filter
等中间操作的无缝衔接。 - 并行处理:通过
.parallelStream()
实现多线程加速。
适用场景
- 数据过滤、转换、聚合:如统计用户订单总金额。
- 复杂逻辑的简化:例如,
flatMap
处理嵌套集合。
五、常见陷阱与解决方案
1. 遍历时修改集合引发的异常
错误代码示例:
List<String> list = new ArrayList<>(Arrays.asList("A", "B"));
for (String s : list) {
if (s.equals("B")) {
list.remove(s); // 触发 ConcurrentModificationException
}
}
原因:增强型 for
循环内部使用迭代器,直接修改集合会破坏迭代器的索引状态。
解决方案:
- 使用 迭代器的
remove()
方法(如前文示例)。 - 将元素存入临时列表后批量删除:
List<String> toRemove = new ArrayList<>(); for (String s : list) { if (s.equals("B")) { toRemove.add(s); } } list.removeAll(toRemove);
2. 并发修改与线程安全
若集合在多线程环境下被遍历和修改,需使用线程安全的集合(如 CopyOnWriteArrayList
)。
六、性能对比与选择建议
遍历方式 | 适用场景 | 性能特点 |
---|---|---|
普通 for 循环 | 需索引操作或性能敏感场景 | 快,但风险较高 |
增强型 for | 只读操作 | 简单,但无法修改集合 |
迭代器 | 需要遍历中修改元素 | 灵活,需手动管理 |
Stream API | 复杂数据处理或并行计算 | 代码简洁,但开销略高 |
选择建议:
- 基础场景:优先使用增强型
for
循环或普通for
。 - 修改集合:必须使用迭代器或
Stream
的removeIf()
方法。 - 复杂操作:结合
Stream
实现函数式编程范式。
七、总结
本文通过 Java 实例 – 集合遍历 的角度,系统梳理了遍历的核心方法、陷阱及优化策略。开发者需根据具体场景选择遍历方式:
- 若需 高效访问索引,普通
for
循环是首选; - 若需 安全修改集合,迭代器是唯一合法途径;
- 若需 简化复杂逻辑,
Stream API
提供了函数式编程的优雅解决方案。
掌握这些方法不仅能提升代码质量,更能为后续学习多线程、数据处理等高级主题打下坚实基础。实践时,建议读者通过编写实际案例(如统计订单金额、过滤用户列表)加深理解,逐步形成自己的“遍历工具箱”。