7.4 集合遍历
在软件开发实践中,经常需要遍历集合中的所有元素,执行某些操作。Java 提供了多种遍历集合的方法,最常用的是 Iterator 接口和 for-each 循环。本文详细介绍这两种方法,并探讨它们的使用场景和技术细节。
7.4.1 Iterator 接口
Iterator 接口是 Java 集合框架的核心接口之一,主要用于迭代访问集合中的元素。与用于存储的 Collection 接口和 Map 接口不同,Iterator 更专注于元素的遍历。
如何使用 Iterator
Iterator 允许程序员以统一的方式访问集合的各种类型,如 ArrayList 和 HashSet。以下是使用 Iterator 遍历集合的典型步骤:
- 获取迭代器:使用集合的
iterator()
方法获得一个迭代器实例。 - 遍历元素:通过调用
hasNext()
方法检查集合中是否还有元素,然后使用next()
方法获取并移动到下一个元素。
示例代码
import java.util.ArrayList;
import java.util.Iterator;
public class Example03 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String obj = it.next();
System.out.println(obj);
}
}
}
此示例展示了创建一个 ArrayList,添加元素,并使用 Iterator 遍历输出每个元素的基本过程。
Iterator 的注意事项
- 类型安全:虽然
next()
方法返回的是 Object 类型,但可以通过泛型提供类型安全的迭代。 - 并发修改异常:在迭代期间尝试直接修改集合(非通过 Iterator 的
remove()
方法),会抛出ConcurrentModificationException
。
避免并发修改异常
在使用 Iterator 遍历时,如果需要修改集合,应该使用 Iterator 自己的 remove()
方法,而不是集合的 remove()
方法。这样做可以避免并发修改异常,因为 Iterator 会在内部调整预期的修改状态。
修改安全的遍历示例
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String obj = it.next();
if ("张三".equals(obj)) {
it.remove(); // 使用迭代器的 remove 方法安全删除元素
}
}
在此示例中,我们安全地在遍历过程中删除了指定的元素。
总结
Iterator 和 for-each 循环提供了在 Java 中遍历集合的强大工具。正确使用这些工具不仅可以提高代码的可读性和灵活性,还可以避免常见的并发修改错误。通过理解和正确应用这些集合遍历技术,开发者可以更有效地管理和使用数据集合。
7.4.2 foreach 循环
Java 的 foreach 循环提供了一种极为简便的方式来遍历数组或集合中的元素。这种循环方式自 JDK 5 引入后,成为了处理集合元素的首选技术之一,特别是在需要读取而非修改集合时。
简介与语法
foreach 循环,又称为增强型 for 循环,通过简化的语法让开发者能够直接遍历数组或集合中的每个元素,而无需手动控制索引或迭代器。其基本语法结构如下:
for (元素类型 临时变量 : 集合对象) {
// 执行语句
}
这种语法避免了使用传统 for 循环时必需的长度控制和元素索引,使得代码更加清晰易读。
应用示例
考虑一个简单的场景,我们有一个包含若干姓名的 ArrayList,并希望遍历输出每一个姓名:
import java.util.ArrayList;
public class Example05 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
for (String name : list) {
System.out.println(name);
}
}
}
在上述代码中,foreach 循环迅速且直接地访问了 list 中的每一个元素,并将其打印出来,无需额外的迭代或索引处理。
foreach 循环的局限性
尽管 foreach 循环在许多场景中都非常有用,它在某些特定情况下显示出了局限性:
- 不可修改元素:foreach 循环无法修改正在遍历的集合的结构,例如添加或删除元素。尝试这样做会抛出
ConcurrentModificationException
。 - 仅值传递:在遍历数组时,foreach 循环使用的是元素的副本而非元素本身。因此,任何对元素的修改都不会反映到原数组中。
示例:修改数组中的元素
public class Example06 {
static String[] strs = {"aaa", "bbb", "ccc"};
public static void main(String[] args) {
for (String str : strs) {
str = "ddd";
}
System.out.println("foreach 循环修改后的数组:" + strs[0] + "," + strs[1] + "," + strs[2]);
for (int i = 0; i < strs.length; i++) {
strs[i] = "ddd";
}
System.out.println("普通 for 循环修改后的数组:" + strs[0] + "," + strs[1] + "," + strs[2]);
}
}
在上述例子中,通过 foreach 循环尝试修改数组元素是无效的,因为它仅修改了临时变量 str
的副本。而普通的 for 循环则能通过直接索引来修改数组,显示出其灵活性。
总结
foreach 循环在 Java 中是处理集合元素的一个极为便捷的工具,特别是在读取操作中。它简化了代码,减少了出错的可能,使得代码更加整洁。然而,开发者在使用时也需要注意其局限性,特别是在需要修改集合内容或性能敏感的环境下。