首页 / Java 入门教程 / 数组遍历

Java 入门教程

数组遍历

本教程共 100 篇 · 第 25 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

JavaJava 入门教程数组遍历求和求最大值

25. 数组遍历

本节目标:掌握用下标 for 和增强 for 两种方式把数组每个元素走一遍,并能顺手写出求最大值、求和、求平均这些经典算法。

把数组建好,下一步多半是”把里面的数挨个看一遍”。这叫遍历。两种主流写法:下标 for 和增强 for。遍历是所有数组算法的地基,求最值、求和、统计都建立在它之上。

下标 for 遍历

知道长度,用下标一个一个取:

int[] scores = {88, 92, 75, 60};
for (int i = 0; i < scores.length; i++) {
    System.out.println("第" + i + "个:" + scores[i]);
}
```bash

`i < scores.length` 是铁律,确保不越界。下标遍历的好处是你知道"当前是第几个",想改值也行。length 是数组自带的属性,不用你自己数,这也是用 length 而不是硬编码数字的原因。

> [!NOTE]
> `scores.length` 是属性,不是方法,所以不带括号。`String` 的 `length()` 是方法,别搞混了。

## 增强 for 遍历

只读不写时,增强 for 更清爽:

```java
for (int s : scores) {
    System.out.println(s);
}

不用管下标、不怕越界。缺点是没有下标,也不知道总共有几个(虽然可以另外数)。当你只关心”每个元素是什么”而不关心”排第几”时,它是最舒服的写法。

Tip

遍历只是”看”,用增强 for;遍历中要改元素或要下标,用下标 for。这一条在数组和集合里都通用,记住能少写很多 bug。

求总和与平均值

int sum = 0;
for (int s : scores) {
    sum += s;
}
double avg = (double) sum / scores.length;
System.out.println("总和:" + sum + ",平均:" + avg);
```bash

注意求平均时把 sum 强转成 double,否则整数相除会丢掉小数。这是新手常踩的坑:两个整数相除结果还是整数,1/20 而不是 0.5

> [!WARNING]
> 整数相除丢小数是高频错误。只要想保留小数,除数或被除数至少一个强转成 double。比如 `(double) sum / len` 或 `sum / (double) len`。

## 求最大值

思路:先假设第一个最大,然后逐个比,遇到更大的就更新。

```java
int max = scores[0];              // 假设第一个最大
for (int i = 1; i < scores.length; i++) {
    if (scores[i] > max) {
        max = scores[i];          // 碰到更大的就替换
    }
}
System.out.println("最大值:" + max);

从下标 1 开始比,因为 scores[0] 已经当成初值了。这种”先设擂主、逐个挑战”的模式在算法里叫擂台法,非常经典。

Warning

求最大值别用 int max = 0 当初值——万一把所有数都是负数,0 反而最大,结果就错了。正确做法是用数组里真实存在的元素(如 scores[0])当起点。

求最小值

和求最大值对称,反过来比就行:

int min = scores[0];
for (int i = 1; i < scores.length; i++) {
    if (scores[i] < min) {
        min = scores[i];
    }
}
System.out.println("最小值:" + min);
```bash

同理,最小值初值也别硬编码一个很大的数,从数组取第一个元素当起点最稳。

## 统计满足条件的个数

```java
int count = 0;
for (int s : scores) {
    if (s >= 60) {
        count++;          // 及格的记一个
    }
}
System.out.println("及格人数:" + count);

这种”设计数器、满足条件就加一”的模式非常常见,记住它。以后过滤集合、统计日志,底层都是这套思路:遍历一遍,符合条件的就累加。

遍历中不要改数组长度

遍历数组时往里加元素或删元素,会ConcurrentModificationException。Java 的数组是定长的,不能增删,只能改值。想增删,用后面学的集合类。

// 错的示范:遍历里不能改数组长度
for (int s : scores) {
    // 没有任何 API 能在这里加/删元素
}
```bash

## 增强 for 的值是副本

增强 for 里拿到的变量是数组元素的副本,改它不影响原数组。

```java
int[] nums = {1, 2, 3};
for (int n : nums) {
    n = n * 2;          // 改的是副本,不是数组里真正的元素
}
System.out.println(nums[0]);    // 还是 1,没变

想改数组里的值,必须用下标 for,通过 nums[i] 直接操作。

Note

增强 for 的”副本”机制对基本类型是直接复制值。对引用类型,复制的是引用(地址),所以修改对象的属性会真的改掉对象本身:

class Student {
    String name;
    Student(String name) { this.name = name; }
}

Student[] students = {new Student("Tom"), new Student("Jerry")};
for (Student s : students) {
    s.name = "改了";    // 改的是对象属性,不是副本引用,所以会影响原数组
}
System.out.println(students[0].name);   // 改了
```bash

## 小心空数组和空引用

下标遍历前先确认数组不是 null,且长度大于 0,否则可能抛异常。

> [!NOTE]
> 遍历空数组(length 为 0)循环体一次都不跑,是安全的。但数组变量本身是 null 时访问 `.length` 会抛 `NullPointerException`。区分"空数组""空引用":前者有对象只是没元素,后者连对象都没有。

```java
int[] empty = new int[0];
System.out.println(empty.length);   // 0,安全

int[] nullArr = null;
System.out.println(nullArr.length); // NullPointerException!

Java 25 LTS 与老写法对照

遍历语法在 Java 25 LTS 没有变化。但老代码里还能看到这些面孔:

写法状态说明
for (int i=0; i<arr.length; i++)✅ 标准下标遍历
for (int x : arr)✅ 标准增强 for,Java 5+
for (int i=0; i<=arr.length-1; i++)⚠️ 啰嗦能跑,但没必要
while (i < arr.length) 手写⚠️ 啰嗦for 已经够用

Java 21 引入了 Stream API 的增强,也可以遍历数组:

import java.util.Arrays;

int[] scores = {88, 92, 75, 60};
Arrays.stream(scores).forEach(System.out::println);
```bash

这是函数式风格,初学了解就行,后面学 Stream 时会细讲。

## 遍历的性能对比

下标 for 和增强 for 在性能上几乎没差别。增强 for 编译后就是下标 for 的语法糖,JVM 优化后一模一样。不用纠结用哪个更快,按"要不要改值、要不要下标"来选就行。

真正影响性能的是遍历内部的逻辑:

```java
// 差的写法:每次循环都调 length
for (int i = 0; i < scores.length; i++) { ... }

// 好的写法:length 提到循环外
int len = scores.length;
for (int i = 0; i < len; i++) { ... }

现代 JVM 对 scores.length 有内联优化,实际差别微乎其微。但”先存 length”在超大规模循环(百万次以上)里确实能省一点。

数组反转算法

反转是遍历的经典应用——首尾对称交换:

int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length / 2; i++) {
    int temp = arr[i];
    arr[i] = arr[arr.length - 1 - i];
    arr[arr.length - 1 - i] = temp;
}
System.out.println(Arrays.toString(arr));   // [5, 4, 3, 2, 1]
```bash

只遍历前半部分,和后半部分交换。注意只跑到 `length / 2`,否则交换两次等于没换。

## 速查表

| 写法 | 适用场景 | 能改元素 | 能取下标 |
|------|----------|----------|----------|
| 下标 for | 要改值、要下标 |||
| 增强 for | 只读遍历 |||

| 典型算法 | 核心思路 |
|----------|----------|
| 求和/平均 | 累加,平均注意强转 double |
| 求最值 | 初值用第一个元素,擂台法比较 |
| 计数 | 满足条件就 count++ |
| 查找 | 遍历匹配,找到可 break |
| 过滤 | 把符合条件的收集到新数组 |
| 反转 | 首尾对称交换,只跑一半 |

## 常见疑问

**Q:增强 forbreak 提前退出吗?**
能。`break` 和 `continue` 在两种 for 里都一样用。找到目标就可以 break,不用傻傻跑完。

**Q:数组遍历用 `i <= length - 1` 可以吗?**
能跑,但没人这么写。`i < length` 更简洁直观。别给自己找麻烦。

**Q:二维数组怎么遍历?**
外层 for 走行,内层 for 走列。下节会详细讲。

**Q:遍历数组时能删元素吗?**
数组是定长的,没有删元素的方法。想"删"只能把后面的元素往前挪,或者用 ArrayList 的 `remove`。

**Q:遍历和迭代有什么区别?**
在 Java 语境里,"遍历""迭代"基本同义。严格来说,迭代(iteration)更强调"用迭代器(Iterator)遍历集合",而遍历(traversal)更通用。数组只能用 for 遍历,集合既可以用 for,也可以用迭代器。