第15章:Java for
章节介绍
章节介绍
for-each循环(也称为增强for循环)是Java 5引入的一个重要特性,它提供了一种更简洁、更安全的方式来遍历数组和集合。本章将深入学习for-each循环的语法、特点、适用场景以及与传统for循环的区别,帮助你掌握这个强大的编程工具。
通过本章的学习,你将能够熟练使用for-each循环来处理各种数据结构,编写更加简洁和可读的代码,同时避免常见的编程错误。
学习目标
学习目标
掌握for-each循环的基本语法
学会for-each循环的正确写法和使用方法
理解for-each循环的适用场景
知道什么时候使用for-each循环,什么时候使用传统for循环
学会遍历数组和集合
掌握使用for-each循环遍历各种数据结构的技巧
了解for-each循环的优缺点
理解for-each循环的限制和最佳使用场景
掌握嵌套for-each循环
学会在多维数组和复杂数据结构中使用嵌套循环
学习最佳实践和常见陷阱
避免常见错误,编写高质量的循环代码
for-each循环基础语法
for-each循环基础语法
基本语法结构
for-each循环的语法比传统for循环更加简洁直观:
for (元素类型 变量名 : 数组或集合) {
// 循环体
// 处理当前元素
}
语法要素说明
- 元素类型:数组或集合中元素的数据类型
- 变量名:用于存储当前遍历元素的变量
- 冒号(:):分隔符,读作"in"
- 数组或集合:要遍历的数据结构
简单示例
// 遍历整数数组
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
// 遍历字符串数组
String[] fruits = {"苹果", "香蕉", "橙子"};
for (String fruit : fruits) {
System.out.println("我喜欢吃: " + fruit);
}
💻 查看完整代码 - 在线IDE体验 for-each循环 vs 传统for循环
for-each循环 vs 传统for循环
对比分析
| 特性 | for-each循环 | 传统for循环 |
|---|---|---|
| 代码简洁性 | ✅ 更简洁,减少样板代码 | ❌ 相对复杂,需要管理索引 |
| 只读遍历 | ✅ 推荐使用 | ✅ 可以使用 |
| 获取索引 | ❌ 不支持 | ✅ 支持 |
| 修改元素 | ❌ 不支持 | ✅ 支持 |
| 反向遍历 | ❌ 不支持 | ✅ 支持 |
| 同时遍历多个数组 | ❌ 不支持 | ✅ 支持 |
| 避免数组越界 | ✅ 自动处理 | ❌ 需要手动控制 |
| 性能 | ✅ 通常更好 | ✅ 相当 |
代码对比示例
int[] numbers = {10, 20, 30, 40, 50};
// 传统for循环
System.out.println("传统for循环:");
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
// for-each循环
System.out.println("\nfor-each循环:");
for (int number : numbers) {
System.out.print(number + " ");
}
for-each循环的优点
- 代码更简洁、可读性更好
- 避免数组越界错误
- 不需要手动管理索引
- 编译器可以进行更好的优化
- 减少出错的可能性
- 意图更明确,专注于处理元素
for-each循环的限制
- 无法获取当前元素的索引
- 无法修改数组或集合中的元素
- 无法反向遍历
- 无法同时遍历多个数组
- 无法在遍历中修改集合结构
- 只能从头到尾顺序遍历
数组和集合遍历
数组和集合遍历
数组遍历
for-each循环可以遍历所有类型的数组:
// 基本数据类型数组
int[] intArray = {1, 2, 3, 4, 5};
for (int value : intArray) {
System.out.println(value);
}
// 对象数组
String[] stringArray = {"Java", "Python", "JavaScript"};
for (String language : stringArray) {
System.out.println("编程语言: " + language);
}
// 二维数组
int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
for (int[] row : matrix) {
for (int element : row) {
System.out.print(element + " ");
}
System.out.println();
}
集合遍历
for-each循环可以遍历所有实现了Iterable接口的集合:
import java.util.*;
// ArrayList遍历
List cities = new ArrayList<>();
cities.add("北京");
cities.add("上海");
cities.add("广州");
for (String city : cities) {
System.out.println("城市: " + city);
}
// HashSet遍历
Set numbers = new HashSet<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
for (Integer number : numbers) {
System.out.println("数字: " + number);
}
// HashMap遍历
Map scores = new HashMap<>();
scores.put("张三", 85);
scores.put("李四", 92);
// 遍历键值对
for (Map.Entry entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 只遍历键
for (String name : scores.keySet()) {
System.out.println("学生: " + name);
}
// 只遍历值
for (Integer score : scores.values()) {
System.out.println("成绩: " + score);
}
提示for-each循环适用于所有实现了Iterable接口的类,包括List、Set、Queue等集合类,但不能直接遍历Map,需要通过Map的视图方法(keySet()、values()、entrySet())来遍历。
嵌套for-each循环
嵌套for-each循环
二维数组遍历
使用嵌套for-each循环可以优雅地处理多维数组:
// 创建一个3x4的二维数组
int[][] matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 使用嵌套for-each循环遍历
System.out.println("矩阵内容:");
for (int[] row : matrix) {
for (int element : row) {
System.out.printf("%4d", element);
}
System.out.println(); // 换行
}
// 计算矩阵所有元素的和
int sum = 0;
for (int[] row : matrix) {
for (int element : row) {
sum += element;
}
}
System.out.println("矩阵元素总和: " + sum);
集合的集合
处理复杂的嵌套数据结构:
// 创建班级学生信息
List> classes = new ArrayList<>();
// 添加各班学生
classes.add(Arrays.asList("张三", "李四", "王五"));
classes.add(Arrays.asList("赵六", "钱七", "孙八", "周九"));
classes.add(Arrays.asList("吴十", "郑十一"));
// 遍历所有班级和学生
int classNumber = 1;
for (List classStudents : classes) {
System.out.println("第" + classNumber++ + "班:");
for (String student : classStudents) {
System.out.println(" - " + student);
}
}
// 统计总学生数
int totalStudents = 0;
for (List classStudents : classes) {
totalStudents += classStudents.size();
}
System.out.println("总学生数: " + totalStudents);
Map中的集合
// 商店商品分类
Map> store = new HashMap<>();
store.put("水果", Arrays.asList("苹果", "香蕉", "橙子"));
store.put("蔬菜", Arrays.asList("白菜", "萝卜", "土豆"));
store.put("肉类", Arrays.asList("猪肉", "牛肉"));
// 遍历商品分类
for (Map.Entry> category : store.entrySet()) {
System.out.println(category.getKey() + "类:");
for (String product : category.getValue()) {
System.out.println(" - " + product);
}
}
常见陷阱和错误
常见陷阱和错误
陷阱1:在for-each循环中修改集合结构这是最常见的错误,会导致ConcurrentModificationException异常。
// ❌ 错误做法:在for-each循环中删除元素
List fruits = new ArrayList<>(Arrays.asList("苹果", "香蕉", "橙子"));
// 这会抛出ConcurrentModificationException
try {
for (String fruit : fruits) {
if (fruit.equals("香蕉")) {
fruits.remove(fruit); // 危险操作!
}
}
} catch (ConcurrentModificationException e) {
System.out.println("错误:在for-each循环中修改集合结构");
}
// ✅ 正确做法:使用Iterator
Iterator iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if (fruit.equals("香蕉")) {
iterator.remove(); // 安全操作
}
}
陷阱2:误以为可以修改数组元素在for-each循环中修改变量不会影响原数组。
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("原始数组: " + Arrays.toString(numbers));
// ❌ 错误理解:这不会修改原数组
for (int num : numbers) {
num *= 2; // 这只是修改了局部变量
}
System.out.println("for-each循环后: " + Arrays.toString(numbers)); // 数组未改变
// ✅ 正确做法:使用传统for循环修改数组
for (int i = 0; i < numbers.length; i++) {
numbers[i] *= 2;
}
System.out.println("传统for循环后: " + Arrays.toString(numbers));
陷阱3:对null集合使用for-each对null引用使用for-each循环会抛出NullPointerException。
List nullList = null;
// ❌ 错误做法:直接使用for-each(会抛出NullPointerException)
try {
for (String item : nullList) {
System.out.println(item);
}
} catch (NullPointerException e) {
System.out.println("错误:对null集合使用for-each");
}
// ✅ 正确做法:先检查null
if (nullList != null) {
for (String item : nullList) {
System.out.println(item);
}
} else {
System.out.println("集合为null,跳过遍历");
}
最佳实践
for-each循环最佳实践
优先使用for-each
对于只读遍历,优先选择for-each循环,代码更简洁安全。
使用有意义的变量名
选择描述性强的变量名,提高代码可读性。
处理null值
在使用for-each循环前检查集合是否为null。
避免复杂计算
将复杂的计算逻辑提取到循环外部,提高性能。
合理选择循环类型
根据需求选择合适的循环类型,不要强行使用for-each。
注意性能影响
对于大数据量,考虑循环的性能影响和优化策略。
性能考虑
性能考虑
数组遍历性能
对于数组,for-each循环和传统for循环的性能基本相当:
int[] largeArray = new int[1000000];
for (int i = 0; i < largeArray.length; i++) {
largeArray[i] = i;
}
// 测试for-each循环性能
long startTime = System.nanoTime();
long sum1 = 0;
for (int value : largeArray) {
sum1 += value;
}
long forEachTime = System.nanoTime() - startTime;
// 测试传统for循环性能
startTime = System.nanoTime();
long sum2 = 0;
for (int i = 0; i < largeArray.length; i++) {
sum2 += largeArray[i];
}
long traditionalTime = System.nanoTime() - startTime;
System.out.println("for-each循环时间: " + forEachTime + " 纳秒");
System.out.println("传统for循环时间: " + traditionalTime + " 纳秒");
集合遍历性能
对于集合,for-each循环通常性能更好,特别是对于LinkedList等非随机访问的集合:
// ArrayList:两种方式性能相当
List arrayList = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
arrayList.add(i);
}
// LinkedList:for-each循环性能更好
List linkedList = new LinkedList<>();
for (int i = 0; i < 10000; i++) {
linkedList.add(i);
}
// ✅ 推荐:for-each循环
for (Integer value : linkedList) {
// 处理元素
}
// ❌ 不推荐:传统for循环(对LinkedList性能很差)
for (int i = 0; i < linkedList.size(); i++) {
Integer value = linkedList.get(i); // O(n)操作!
}
性能提示对于数组,两种循环性能相当,选择for-each提高可读性对于ArrayList,两种循环性能相当对于LinkedList,for-each循环性能明显更好对于Set和其他集合,使用for-each循环避免在循环中进行复杂的计算和对象创建
实际应用场景
实际应用场景
数据处理
// 计算学生平均分
List scores = Arrays.asList(85, 92, 78, 96, 88);
double sum = 0;
for (Integer score : scores) {
sum += score;
}
double average = sum / scores.size();
System.out.printf("平均分: %.2f\n", average);
// 查找最高分
int maxScore = scores.get(0);
for (Integer score : scores) {
if (score > maxScore) {
maxScore = score;
}
}
System.out.println("最高分: " + maxScore);
字符串处理
// 统计字符串长度
List words = Arrays.asList("Java", "Python", "JavaScript", "C++");
int totalLength = 0;
for (String word : words) {
totalLength += word.length();
}
System.out.println("总字符数: " + totalLength);
// 查找包含特定字符的字符串
String searchChar = "a";
List matchingWords = new ArrayList<>();
for (String word : words) {
if (word.toLowerCase().contains(searchChar)) {
matchingWords.add(word);
}
}
System.out.println("包含'" + searchChar + "'的单词: " + matchingWords);
对象处理
// 学生类示例
class Student {
private String name;
private int age;
private double score;
// 构造函数和getter方法...
}
// 处理学生列表
List students = Arrays.asList(
new Student("张三", 20, 85.5),
new Student("李四", 22, 92.0),
new Student("王五", 19, 78.5)
);
// 查找优秀学生
System.out.println("优秀学生名单(90分以上):");
for (Student student : students) {
if (student.getScore() >= 90) {
System.out.println("- " + student.getName() + ": " + student.getScore());
}
}
// 计算平均年龄
double totalAge = 0;
for (Student student : students) {
totalAge += student.getAge();
}
double averageAge = totalAge / students.size();
System.out.printf("平均年龄: %.1f岁\n", averageAge);
章节总结
章节总结
核心要点
- 语法简洁:for-each循环提供了更简洁的遍历语法
- 安全性高:自动处理数组边界,避免越界错误
- 适用范围:支持数组和所有实现Iterable接口的集合
- 性能优秀:通常与传统for循环性能相当或更好
- 有限制性:无法获取索引、修改元素或反向遍历
使用建议
- 优先使用for-each循环进行只读遍历
- 需要索引或修改元素时使用传统for循环
- 注意处理null值和集合修改的陷阱
- 选择有意义的变量名提高代码可读性
- 根据具体需求选择合适的循环类型
下一步学习
掌握了for-each循环后,建议继续学习:
- while循环和do-while循环
- 循环控制语句(break和continue)
- Java 8的Stream API和Lambda表达式
- 集合框架的深入使用