Java 实例 – 集合遍历:从入门到精通的实战指南
在 Java 开发中,集合(Collection)是处理数据最常用的数据结构之一。无论是从数据库读取用户列表,还是处理配置项、日志记录,我们几乎每天都在与集合打交道。而集合的核心操作之一,就是遍历——也就是逐个访问集合中的每一个元素。
如果你刚接触 Java,可能会觉得“遍历”听起来很简单:for 循环不就能搞定?但随着项目复杂度上升,你会遇到并发修改异常、性能瓶颈、代码冗长等问题。这时候,掌握多种遍历方式,理解每种方式的适用场景,就变得至关重要。
本文将以实际代码为例,带你系统掌握 Java 实例 – 集合遍历 的各种方法,从基础到进阶,帮你写出更优雅、更安全、更高效的代码。
为什么要学习集合遍历?
想象一下你有一本厚厚的通讯录,里面记录了 1000 个联系人。现在你要找出所有姓“张”的人。如果逐个翻页查看,效率极低;但如果你能用“快速查找”功能,瞬间筛选出结果,那效率就高多了。
集合遍历的本质,就是“快速查找”工具的使用。不同的遍历方式,就像不同的查找工具。有的快但不能修改,有的灵活但容易出错。掌握它们,才能在开发中游刃有余。
1. for-each 循环:最简洁的遍历方式
for-each 循环(也叫增强 for 循环)是 Java 5 引入的语法糖,它让遍历集合变得极其简单。它是大多数初学者首选的方式。
代码示例:遍历 List
import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
// 创建一个包含 5 个名字的列表
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("Diana");
names.add("Eve");
// 使用 for-each 循环遍历集合
for (String name : names) {
System.out.println("当前名字:" + name);
}
}
}
中文注释说明:
List<String> names:声明一个字符串类型的列表,用于存储名字。names.add(...):向列表中添加元素。for (String name : names):这是 for-each 循环语法,name是当前元素的变量名,names是被遍历的集合。System.out.println(...):打印当前名字。
优点:
- 代码简洁,可读性强。
- 避免手动索引操作,不容易出错。
缺点:
- 无法在遍历时修改集合(如删除元素),否则会抛出
ConcurrentModificationException。
2. Iterator 迭代器:安全修改集合的利器
当你需要在遍历过程中删除或添加元素时,for-each 就会“罢工”,抛出异常。这时,Iterator 就派上用场了。
代码示例:使用 Iterator 删除偶数
import java.util.*;
public class IteratorExample {
public static void main(String[] args) {
// 创建一个整数列表
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);
// 获取迭代器
Iterator<Integer> iterator = numbers.iterator();
// 使用 while 循环配合 Iterator
while (iterator.hasNext()) {
Integer num = iterator.next(); // 获取下一个元素
// 如果是偶数,就删除
if (num % 2 == 0) {
iterator.remove(); // 安全删除当前元素
}
}
// 输出结果
System.out.println("删除偶数后的列表:" + numbers);
}
}
中文注释说明:
iterator.hasNext():判断是否还有下一个元素。iterator.next():返回下一个元素并移动指针。iterator.remove():唯一安全删除元素的方法,必须通过迭代器调用。
重要提醒:
- 不能在 for-each 中调用
remove(),否则会触发并发修改异常。 Iterator.remove()是唯一合法的删除方式。
3. 传统 for 循环:索引控制的灵活性
当需要根据索引进行操作,比如访问前一个元素、跳过某些位置、或执行条件判断时,传统 for 循环就非常有优势。
代码示例:遍历 List 并访问前后元素
import java.util.*;
public class TraditionalForExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子", "葡萄", "草莓");
// 使用传统 for 循环,通过索引遍历
for (int i = 0; i < fruits.size(); i++) {
String current = fruits.get(i); // 获取当前元素
String next = ""; // 声明下一个元素
// 判断是否是最后一个元素
if (i < fruits.size() - 1) {
next = fruits.get(i + 1); // 获取下一个元素
} else {
next = "无";
}
System.out.println("当前水果:" + current + ",下一个水果:" + next);
}
}
}
中文注释说明:
fruits.size():获取集合长度。fruits.get(i):通过索引获取元素,适用于 List。i < fruits.size() - 1:防止索引越界。
适用场景:
- 需要访问前后元素。
- 需要跳过某些索引(如每隔一个处理一次)。
- 需要倒序遍历。
4. Stream API:函数式编程的优雅之选
Java 8 引入的 Stream API 让集合遍历进入函数式编程时代。它不只遍历,还能过滤、映射、聚合,代码更简洁、更强大。
代码示例:使用 Stream 筛选并打印名字长度大于 4 的人
import java.util.*;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana", "Eve", "Frank");
// 使用 Stream API 进行链式操作
List<String> filteredNames = names.stream()
.filter(name -> name.length() > 4) // 筛选出长度大于 4 的名字
.collect(Collectors.toList()); // 收集结果为列表
// 遍历输出
filteredNames.forEach(name -> System.out.println("名字:" + name));
}
}
中文注释说明:
stream():将集合转换为流。filter(...):过滤条件,只保留满足条件的元素。Collectors.toList():将流中的元素收集回列表。forEach(...):遍历并执行操作。
优势:
- 支持链式调用,逻辑清晰。
- 可并行处理(
parallelStream()),适合大数据。 - 代码更接近自然语言,可读性高。
5. Map 遍历:键值对的处理方式
Map 是键值对集合,遍历方式也更丰富。常见有三种方式:entrySet、keySet、forEach。
代码示例:遍历 Map 的三种方式
import java.util.*;
public class MapTraversalExample {
public static void main(String[] args) {
Map<String, Integer> scores = new HashMap<>();
scores.put("张三", 85);
scores.put("李四", 92);
scores.put("王五", 78);
// 方式一:使用 entrySet + for-each
System.out.println("方式一:entrySet 遍历");
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + " 的成绩是:" + entry.getValue());
}
// 方式二:使用 keySet + for-each
System.out.println("\n方式二:keySet 遍历");
for (String key : scores.keySet()) {
System.out.println(key + " 的成绩是:" + scores.get(key));
}
// 方式三:使用 forEach + Lambda
System.out.println("\n方式三:forEach + Lambda");
scores.forEach((name, score) -> System.out.println(name + " 的成绩是:" + score));
}
}
中文注释说明:
entrySet():返回键值对集合,适合同时获取 key 和 value。keySet():返回所有键的集合,适合只关心 key。forEach(...):Java 8 新增,支持 Lambda 表达式。
推荐使用:
- 需要同时获取 key 和 value:用
entrySet。 - 只需要 key:用
keySet。 - 代码简洁且不关心性能:用
forEach。
总结与建议
通过本文的 Java 实例 – 集合遍历 实战讲解,我们系统学习了五种主要遍历方式:
| 方式 | 适用场景 | 是否支持修改 | 优点 | 缺点 |
|---|---|---|---|---|
| for-each | 简单遍历 | 否(会抛异常) | 代码简洁 | 不能安全删除 |
| Iterator | 需要删除元素 | 是 | 安全删除 | 语法稍复杂 |
| 传统 for 循环 | 索引操作 | 是 | 灵活控制 | 代码较长 |
| Stream API | 数据处理链 | 否(默认) | 功能强大,可读性高 | 学习成本略高 |
| Map 遍历 | 键值对处理 | 视方式而定 | 多样选择 | 选择需谨慎 |
实用建议:
- 初学者优先掌握 for-each 和 Iterator。
- 中级开发者应熟悉 Stream API,提升代码质量。
- 避免在遍历时直接修改集合,除非使用 Iterator.remove()。
- Map 遍历时优先使用 entrySet,避免重复 get。
掌握这些技巧后,你将不再为“如何遍历集合”而烦恼。每一次遍历,都是一次对代码质量的提升。
记住,代码不仅仅是“能运行”,更要“好维护、易扩展”。而这一切,都始于对集合遍历的深刻理解。