Appearance
14.5 集合遍历
集合遍历的概念
集合遍历是指按照一定的顺序访问集合中的每个元素的过程。Java 集合框架提供了多种遍历集合的方法,每种方法都有其适用场景和优缺点。
1. Collection 集合的遍历
1.1 增强 for 循环
特点:语法简洁,适用于不需要修改集合的场景
示例:
java
Collection<String> collection = new ArrayList<>();
collection.add("Java");
collection.add("Python");
collection.add("C++");
for (String element : collection) {
System.out.println(element);
}1.2 迭代器
特点:可以在遍历过程中修改集合(通过迭代器的 remove() 方法)
示例:
java
Collection<String> collection = new ArrayList<>();
collection.add("Java");
collection.add("Python");
collection.add("C++");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
// 在遍历过程中删除元素
if (element.equals("Python")) {
iterator.remove();
}
}
System.out.println("遍历后集合: " + collection); // [Java, C++]1.3 forEach 方法(Java 8+)
特点:使用 lambda 表达式,代码简洁
示例:
java
Collection<String> collection = new ArrayList<>();
collection.add("Java");
collection.add("Python");
collection.add("C++");
collection.forEach(element -> System.out.println(element));
// 或使用方法引用
collection.forEach(System.out::println);2. List 集合的遍历
2.1 索引遍历
特点:可以通过索引直接访问元素,适用于需要索引的场景
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
for (int i = 0; i < list.size(); i++) {
String element = list.get(i);
System.out.println("索引 " + i + ": " + element);
}2.2 增强 for 循环
特点:语法简洁,适用于不需要索引的场景
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
for (String element : list) {
System.out.println(element);
}2.3 迭代器
特点:可以在遍历过程中修改集合
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}2.4 ListIterator
特点:可以双向遍历,可以在遍历过程中修改集合(添加、删除、修改元素)
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
// 正向遍历
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String element = listIterator.next();
System.out.println("正向: " + element);
// 在遍历过程中添加元素
if (element.equals("Python")) {
listIterator.add("JavaScript");
}
}
System.out.println("添加元素后: " + list); // [Java, Python, JavaScript, C++]
// 反向遍历
while (listIterator.hasPrevious()) {
String element = listIterator.previous();
System.out.println("反向: " + element);
}2.5 forEach 方法(Java 8+)
特点:使用 lambda 表达式,代码简洁
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
list.forEach(element -> System.out.println(element));3. Set 集合的遍历
3.1 增强 for 循环
特点:语法简洁
示例:
java
Set<String> set = new HashSet<>();
set.add("Java");
set.add("Python");
set.add("C++");
for (String element : set) {
System.out.println(element);
}3.2 迭代器
特点:可以在遍历过程中修改集合
示例:
java
Set<String> set = new HashSet<>();
set.add("Java");
set.add("Python");
set.add("C++");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
// 在遍历过程中删除元素
if (element.equals("Python")) {
iterator.remove();
}
}
System.out.println("遍历后集合: " + set); // [Java, C++]3.3 forEach 方法(Java 8+)
特点:使用 lambda 表达式,代码简洁
示例:
java
Set<String> set = new HashSet<>();
set.add("Java");
set.add("Python");
set.add("C++");
set.forEach(element -> System.out.println(element));4. Map 集合的遍历
4.1 遍历键
特点:只遍历键,适用于只需要键的场景
示例:
java
Map<String, Integer> map = new HashMap<>();
map.put("Java", 95);
map.put("Python", 85);
map.put("C++", 90);
for (String key : map.keySet()) {
System.out.println("Key: " + key);
}4.2 遍历值
特点:只遍历值,适用于只需要值的场景
示例:
java
Map<String, Integer> map = new HashMap<>();
map.put("Java", 95);
map.put("Python", 85);
map.put("C++", 90);
for (int value : map.values()) {
System.out.println("Value: " + value);
}4.3 遍历键值对
特点:同时遍历键和值,适用于需要两者的场景
示例:
java
Map<String, Integer> map = new HashMap<>();
map.put("Java", 95);
map.put("Python", 85);
map.put("C++", 90);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}4.4 迭代器遍历
特点:可以在遍历过程中修改 Map
示例:
java
Map<String, Integer> map = new HashMap<>();
map.put("Java", 95);
map.put("Python", 85);
map.put("C++", 90);
// 遍历键值对
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
// 在遍历过程中删除元素
if (entry.getKey().equals("Python")) {
iterator.remove();
}
}
System.out.println("遍历后 Map: " + map); // {Java=95, C++=90}4.5 forEach 方法(Java 8+)
特点:使用 lambda 表达式,代码简洁
示例:
java
Map<String, Integer> map = new HashMap<>();
map.put("Java", 95);
map.put("Python", 85);
map.put("C++", 90);
map.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));5. 遍历性能比较
5.1 List 遍历性能
| 遍历方式 | 适用场景 | 性能 |
|---|---|---|
| 索引遍历 | 需要索引的场景 | ArrayList 快,LinkedList 慢 |
| 增强 for 循环 | 不需要索引的场景 | 快 |
| 迭代器 | 需要在遍历过程中修改集合 | 快 |
| forEach 方法 | 代码简洁性优先 | 快 |
5.2 Set 遍历性能
| 遍历方式 | 适用场景 | 性能 |
|---|---|---|
| 增强 for 循环 | 不需要修改集合 | 快 |
| 迭代器 | 需要在遍历过程中修改集合 | 快 |
| forEach 方法 | 代码简洁性优先 | 快 |
5.3 Map 遍历性能
| 遍历方式 | 适用场景 | 性能 |
|---|---|---|
| 遍历键值对 | 需要同时使用键和值 | 快 |
| 遍历键 | 只需要键 | 快 |
| 遍历值 | 只需要值 | 快 |
| forEach 方法 | 代码简洁性优先 | 快 |
6. 遍历中的常见问题
6.1 并发修改异常
症状:在增强 for 循环中修改集合,导致 ConcurrentModificationException
解决方案:使用迭代器的 remove() 方法,或使用 ListIterator 进行修改
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
// 错误:在增强 for 循环中修改集合
// for (String element : list) {
// if (element.equals("Python")) {
// list.remove(element);
// }
// }
// 正确:使用迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("Python")) {
iterator.remove(); // 使用迭代器的 remove 方法
}
}
// 正确:使用 ListIterator
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String element = listIterator.next();
if (element.equals("Python")) {
listIterator.remove();
listIterator.add("JavaScript"); // 可以添加元素
}
}6.2 遍历顺序问题
症状:遍历顺序与预期不符
解决方案:选择合适的集合实现
示例:
java
// HashSet 不保证遍历顺序
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("C++");
System.out.println("HashSet 遍历顺序: " + hashSet);
// LinkedHashSet 保证插入顺序
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Java");
linkedHashSet.add("Python");
linkedHashSet.add("C++");
System.out.println("LinkedHashSet 遍历顺序: " + linkedHashSet);
// TreeSet 保证自然顺序
Set<String> treeSet = new TreeSet<>();
treeSet.add("Java");
treeSet.add("Python");
treeSet.add("C++");
System.out.println("TreeSet 遍历顺序: " + treeSet);6.3 遍历中的空指针异常
症状:遍历过程中遇到 NullPointerException
解决方案:在遍历前检查集合是否为 null,在遍历过程中检查元素是否为 null
示例:
java
// 检查集合是否为 null
List<String> list = null;
if (list != null) {
for (String element : list) {
System.out.println(element);
}
}
// 检查元素是否为 null
List<String> list2 = new ArrayList<>();
list2.add("Java");
list2.add(null);
list2.add("C++");
for (String element : list2) {
if (element != null) {
System.out.println(element.length());
}
}7. 示例:集合遍历的应用
7.1 学生成绩统计
java
import java.util.ArrayList;
import java.util.List;
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
@Override
public String toString() {
return "Student{name='" + name + "', score=" + score + "}";
}
}
public class StudentScoreStatistics {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 95));
students.add(new Student("Bob", 85));
students.add(new Student("Charlie", 90));
students.add(new Student("David", 80));
students.add(new Student("Eve", 92));
// 计算平均成绩
int totalScore = 0;
for (Student student : students) {
totalScore += student.getScore();
}
double averageScore = (double) totalScore / students.size();
System.out.println("平均成绩: " + averageScore);
// 找出最高分
int maxScore = Integer.MIN_VALUE;
String topStudentName = "";
for (Student student : students) {
if (student.getScore() > maxScore) {
maxScore = student.getScore();
topStudentName = student.getName();
}
}
System.out.println("最高分: " + maxScore + " (" + topStudentName + ")");
// 找出不及格的学生
System.out.println("不及格的学生:");
for (Student student : students) {
if (student.getScore() < 60) {
System.out.println(student);
}
}
// 使用 forEach 方法打印所有学生
System.out.println("所有学生:");
students.forEach(System.out::println);
}
}7.2 单词频率统计
java
import java.util.HashMap;
import java.util.Map;
public class WordFrequency {
public static void main(String[] args) {
String text = "Java is a programming language. Java is widely used. Java is popular.";
// 分割文本为单词
String[] words = text.toLowerCase().split("\\W+");
// 统计单词频率
Map<String, Integer> frequencyMap = new HashMap<>();
for (String word : words) {
if (!word.isEmpty()) {
frequencyMap.put(word, frequencyMap.getOrDefault(word, 0) + 1);
}
}
// 打印单词频率
System.out.println("单词频率统计:");
for (Map.Entry<String, Integer> entry : frequencyMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 使用 forEach 方法打印
System.out.println("\n使用 forEach 方法:");
frequencyMap.forEach((key, value) -> System.out.println(key + ": " + value));
}
}7.3 集合转换
java
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CollectionConversion {
public static void main(String[] args) {
// List 转 Set(去重)
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Java"); // 重复元素
list.add("C++");
System.out.println("原始 List: " + list);
Set<String> set = new HashSet<>(list);
System.out.println("转换为 Set: " + set); // 去重
// Set 转 List
List<String> newList = new ArrayList<>(set);
System.out.println("转换为 List: " + newList);
}
}8. 最佳实践
选择合适的遍历方式:根据具体需求选择合适的遍历方式
避免并发修改:在遍历过程中,使用迭代器的方法进行修改
注意空指针异常:在遍历前检查集合是否为 null,在遍历过程中检查元素是否为 null
考虑性能:对于不同的集合类型,选择性能最优的遍历方式
代码简洁性:在不需要修改集合的场景下,使用增强 for 循环或 forEach 方法
利用 lambda 表达式:在 Java 8+ 中,使用 lambda 表达式和方法引用使代码更简洁
选择合适的集合实现:根据遍历顺序的需求,选择合适的集合实现
总结
Java 集合框架提供了多种遍历集合的方法,每种方法都有其适用场景:
Collection 集合:增强 for 循环、迭代器、forEach 方法
List 集合:索引遍历、增强 for 循环、迭代器、ListIterator、forEach 方法
Set 集合:增强 for 循环、迭代器、forEach 方法
Map 集合:遍历键、遍历值、遍历键值对、迭代器、forEach 方法
在遍历集合时,需要注意:
- 避免在增强 for 循环中修改集合
- 注意空指针异常
- 根据具体需求选择合适的遍历方式
- 考虑遍历的性能和顺序
通过合理选择和使用遍历方法,可以提高代码的效率和可维护性。
