六:数组——批量管理数据

一、回顾与本篇目标

上一篇你学会了循环——用 for 循环重复执行代码,用 while 循环在条件成立时持续执行。你现在可以写程序自动计算 1 到 100 的和、判断一个数是不是素数。

但如果你要存储全班 50 个学生的成绩,怎么办?定义 50 个变量?int score1, score2, score3... 那如果要计算平均分,你需要把 50 个变量一个一个写出来加起来。这显然不现实。更麻烦的是,如果学生人数是运行时才知道的(比如用户输入人数),你根本没办法提前定义那么多变量。

你需要数组——一种能一次性存储多个相同类型数据的结构。数组就像一排贴了编号的储物柜——每个柜子存一个数据,通过编号(索引)来存取。

本篇的目标:

  1. 理解数组是什么——连续存放的多个数据
  2. 学会数组的声明、初始化和访问
  3. 掌握用循环遍历数组
  4. 学会数组的常见操作:求和、最大值、查找
  5. 理解二维数组

二、数组是什么——一排编号的储物柜

数组是同一种类型数据的集合。你可以把数组想象成学校里一排带编号的储物柜:

  • 每个柜子大小相同(都存同一种类型的数据,比如都是 int)。
  • 每个柜子有一个编号(从 0 开始),叫索引下标
  • 通过编号可以快速找到任何一个柜子里的东西。
  • 柜子的总数在创建时就定好了,之后不能改变。

在 Java 中,数组是固定大小的——创建时指定长度,之后这个长度不能变。如果需要能动态增减的容器,后面会学 ArrayList

三、数组的声明和创建

3.1 声明数组变量

语法:数据类型[] 数组名;

int[] scores;        // 声明一个存放整数的数组,名字叫 scores
double[] prices;     // 声明一个存放小数的数组
String[] names;      // 声明一个存放字符串的数组
char[] letters;      // 声明一个存放字符的数组

声明只是告诉编译器“这个变量将来会指向一个数组”,此时数组还不存在,就像你画了一个柜子区的规划图,但柜子还没造出来。

3.2 创建数组——让柜子真正存在

new 关键字真正创建数组,必须指定长度

scores = new int[5];    // 创建一个长度为 5 的整数数组
prices = new double[10]; // 创建一个长度为 10 的小数数组
names = new String[3];   // 创建一个长度为 3 的字符串数组

数组创建后,每个位置都有一个默认值

  • 整数类型(intbyteshortlong):0
  • 小数类型(floatdouble):0.0
  • 字符类型(char):‘\u0000’(空字符)
  • 布尔类型(boolean):false
  • 引用类型(String 等):null(表示空,不指向任何对象)

3.3 声明和创建一步完成

int[] scores = new int[5];     // 声明并创建,长度为 5
double[] prices = new double[3]; // 声明并创建,长度为 3
String[] names = new String[4];  // 声明并创建,长度为 4

3.4 创建的同时赋值——静态初始化

如果你已经知道数组里要存什么数据,可以在创建时直接列出所有值:

int[] scores = {85, 92, 78, 95, 88};          // 长度为 5,值已填入
double[] prices = {19.9, 29.9, 39.9};          // 长度为 3
String[] fruits = {"苹果", "香蕉", "橘子"};     // 长度为 3

// 也可以先声明,再用这种方式赋值(但只能在声明的同时使用)
int[] numbers;
numbers = new int[]{10, 20, 30, 40};  // 先声明,后赋值用 new int[]{...}

使用花括号 {} 初始化时,不需要写数组长度——Java 会根据你列出的值的个数自动确定长度。

四、访问数组元素——通过索引

数组的索引从 0 开始。一个长度为 N 的数组,有效索引是 0 到 N-1

int[] scores = {85, 92, 78, 95, 88};

// 索引:     0   1   2   3   4
// 值:      85  92  78  95  88

读取数组元素

System.out.println(scores[0]);   // 85 —— 第一个元素
System.out.println(scores[1]);   // 92 —— 第二个元素
System.out.println(scores[4]);   // 88 —— 第五个元素(最后一个)

修改数组元素

scores[0] = 100;                 // 把第一个元素改成 100
scores[3] = scores[3] + 5;       // 把第四个元素加 5
System.out.println(scores[0]);   // 100
System.out.println(scores[3]);   // 100

获取数组长度

数组有一个内置属性 length,表示数组的长度(元素个数):

int[] scores = {85, 92, 78, 95, 88};
System.out.println("数组长度:" + scores.length);  // 5

注意:length 是属性,不是方法,后面不加括号。这和 Stringlength() 方法不同(后面会讲)。

数组下标越界

如果访问的索引超出了数组的有效范围,Java 会抛出 ArrayIndexOutOfBoundsException(数组下标越界异常),程序会报错并停止:

int[] scores = {85, 92, 78};
// System.out.println(scores[5]);  // 运行时错误!索引 5 超出了长度 3 的范围

这是最常见的数组错误之一。记住:索引范围是 0 到 length-1

五、遍历数组——用循环处理每个元素

数组和循环是天生的搭档。数组存数据,循环逐个处理数据。

5.1 用 for 循环按索引遍历

public class ArrayTraversal {
    public static void main(String[] args) {
        int[] scores = {85, 92, 78, 95, 88};

        System.out.println("所有成绩:");
        for (int i = 0; i < scores.length; i++) {
            System.out.println("学生 " + (i + 1) + ":" + scores[i] + " 分");
        }
    }
}

输出:

所有成绩:
学生 1:85 分
学生 2:92 分
学生 3:78 分
学生 4:95 分
学生 5:88 分

关键点

  • i = 0:从第一个元素开始。
  • i < scores.length严格小于长度,不能写成 <=。因为长度为 5 时,最大索引是 4,i <= 5 会让 i 等于 5 时访问 scores[5],导致越界。
  • i++:每次索引加 1,访问下一个元素。

5.2 用增强 for 循环(for-each)遍历

Java 提供了一种更简洁的遍历方式——增强 for 循环。你不需要手动管理索引,直接拿到每个元素的值:

int[] scores = {85, 92, 78, 95, 88};

for (int score : scores) {   // 读作:对于 scores 中的每一个 score
    System.out.println(score);
}

这个循环的意思是:把 scores 数组中的每个元素依次赋给变量 score,然后执行循环体。 它等价于前面的 for 循环,但更简洁。

增强 for 循环的局限

  • 你不知道当前元素的索引(想知道是第几个元素时,用普通 for 循环)。
  • 你不能修改数组元素的值(修改 score 变量只是修改了临时变量的副本,不影响原数组)。

5.3 两种遍历方式的选择

  • 需要索引(比如显示“第几个学生”)→ 用普通 for 循环。
  • 需要修改数组元素 → 用普通 for 循环,通过索引修改。
  • 只读取值,不需要索引 → 用增强 for 循环,更简洁。

六、数组的常见操作

数组操作是编程中最频繁的操作之一。以下是几个最常用的模式:

6.1 数组求和与平均值

public class ArraySum {
    public static void main(String[] args) {
        int[] scores = {85, 92, 78, 95, 88};
        
        int total = 0;
        for (int score : scores) {
            total += score;  // total = total + score
        }
        
        double average = (double) total / scores.length;  // 转成 double 保留小数
        
        System.out.println("总分:" + total);
        System.out.println("平均分:" + average);
    }
}

6.2 找最大值和最小值

public class ArrayMaxMin {
    public static void main(String[] args) {
        int[] scores = {85, 92, 78, 95, 88};
        
        int max = scores[0];  // 假设第一个是最大值
        int min = scores[0];  // 假设第一个是最小值
        
        for (int i = 1; i < scores.length; i++) {
            if (scores[i] > max) {
                max = scores[i];  // 找到更大的,更新最大值
            }
            if (scores[i] < min) {
                min = scores[i];  // 找到更小的,更新最小值
            }
        }
        
        System.out.println("最高分:" + max);  // 95
        System.out.println("最低分:" + min);  // 78
    }
}

6.3 查找指定元素

public class ArraySearch {
    public static void main(String[] args) {
        int[] scores = {85, 92, 78, 95, 88};
        int target = 95;
        int foundIndex = -1;  // -1 表示没找到
        
        for (int i = 0; i < scores.length; i++) {
            if (scores[i] == target) {
                foundIndex = i;
                break;  // 找到了就不继续找了
            }
        }
        
        if (foundIndex != -1) {
            System.out.println("找到 " + target + ",位置在索引 " + foundIndex);
        } else {
            System.out.println("未找到 " + target);
        }
    }
}

七、二维数组——数组的数组

二维数组可以理解为一张表格——有行和列。比如一个班级的成绩表,每行是一个学生,每列是一个科目。

7.1 声明和创建二维数组

// 声明并创建:3 行 4 列的二维数组
int[][] matrix = new int[3][4];

// 创建并直接赋值
int[][] grid = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

7.2 访问二维数组元素

两个索引——第一个是行,第二个是列:

int[][] grid = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

System.out.println(grid[0][0]);  // 1 —— 第 0 行第 0 列
System.out.println(grid[1][2]);  // 7 —— 第 1 行第 2 列
System.out.println(grid[2][3]);  // 12 —— 第 2 行第 3 列

// 获取行数和列数
System.out.println("行数:" + grid.length);         // 3
System.out.println("列数:" + grid[0].length);      // 4(第 0 行的长度)

7.3 遍历二维数组——嵌套循环

int[][] grid = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 外层循环遍历行
for (int i = 0; i < grid.length; i++) {
    // 内层循环遍历这一行的每一列
    for (int j = 0; j < grid[i].length; j++) {
        System.out.print(grid[i][j] + "\t");
    }
    System.out.println();  // 每行结束换行
}

输出:

1   2   3   4
5   6   7   8
9   10  11  12

八、综合演示:学生成绩统计

下面这个程序综合运用了数组、循环、条件判断,计算学生成绩的总分、平均分、最高分和等级:

public class ScoreAnalyzer {
    public static void main(String[] args) {
        // 学生姓名和成绩
        String[] names = {"张三", "李四", "王五", "赵六", "孙七"};
        int[] scores = {85, 92, 78, 95, 60};

        System.out.println("========== 成绩统计 ==========");

        // 1. 输出每个学生的成绩和等级
        for (int i = 0; i < scores.length; i++) {
            String grade;
            if (scores[i] >= 90) {
                grade = "优秀";
            } else if (scores[i] >= 80) {
                grade = "良好";
            } else if (scores[i] >= 70) {
                grade = "中等";
            } else if (scores[i] >= 60) {
                grade = "及格";
            } else {
                grade = "不及格";
            }
            System.out.println(names[i] + ":" + scores[i] + " 分 —— " + grade);
        }

        // 2. 计算总分和平均分
        int total = 0;
        for (int score : scores) {
            total += score;
        }
        double average = (double) total / scores.length;

        // 3. 找最高分和最低分及其对应的学生
        int maxIndex = 0;
        int minIndex = 0;
        for (int i = 1; i < scores.length; i++) {
            if (scores[i] > scores[maxIndex]) {
                maxIndex = i;
            }
            if (scores[i] < scores[minIndex]) {
                minIndex = i;
            }
        }

        // 4. 统计各等级人数
        int excellentCount = 0;  // 优秀人数
        int passCount = 0;       // 及格及以上人数
        for (int score : scores) {
            if (score >= 90) excellentCount++;
            if (score >= 60) passCount++;
        }

        System.out.println("-----------------------------");
        System.out.println("总分:" + total);
        System.out.println("平均分:" + average);
        System.out.println("最高分:" + scores[maxIndex] + "(" + names[maxIndex] + ")");
        System.out.println("最低分:" + scores[minIndex] + "(" + names[minIndex] + ")");
        System.out.println("优秀人数:" + excellentCount);
        System.out.println("及格人数:" + passCount + " / " + scores.length);
        System.out.println("==============================");
    }
}

输出:

========== 成绩统计 ==========
张三:85 分 —— 良好
李四:92 分 —— 优秀
王五:78 分 —— 中等
赵六:95 分 —— 优秀
孙七:60 分 —— 及格
-----------------------------
总分:410
平均分:82.0
最高分:95(赵六)
最低分:60(孙七)
优秀人数:2
及格人数:5 / 5
==============================

代码设计要点

  • 两个平行的数组 namesscores 存储学生姓名和成绩,通过同一个索引关联。
  • 找最大值时,不直接存储最大值,而是存储最大值所在的索引——这样既能输出最高分,也能输出对应的学生姓名。
  • 多个统计任务(总分、平均分、最高最低分、等级人数)各自独立成一个循环,逻辑清晰。

九、本篇动手练习

练习 1:数组反转

新建 Practice6_1.java。创建一个包含 10 个整数的数组并初始化。写代码把这个数组的元素顺序反转(第一个和最后一个交换,第二个和倒数第二个交换……),输出反转前后的数组。

练习 2:冒泡排序

新建 Practice6_2.java。创建一个无序的整数数组。用冒泡排序算法把数组从小到大排序。冒泡排序的逻辑:相邻元素两两比较,如果前面的比后面的大就交换。重复多轮,直到没有任何交换发生。输出排序前后的数组。

练习 3:矩阵转置

新建 Practice6_3.java。创建一个 3×3 的二维数组。输出原矩阵和转置后的矩阵。转置就是把行变成列、列变成行——matrix[i][j] 变成 matrix[j][i]

练习 4:统计字母频率

新建 Practice6_4.java。声明一个字符串变量 String text = "hello world";。创建一个长度为 26 的整数数组,统计这段文字中 26 个英文字母各出现了多少次(忽略大小写)。提示:字符可以通过 ch - 'a' 转换成 0-25 的索引。

十、本篇小结

这一篇你学会了 Java 中最重要的数据结构之一——数组:

  • 数组是什么:同一类型数据的集合,通过索引(从 0 开始)访问。长度在创建时确定,之后不能改变。
  • 声明和创建数据类型[] 数组名 = new 数据类型[长度]; 或者直接用花括号初始化 {值1, 值2, ...}
  • 访问元素:通过 数组名[索引] 读写。索引从 0 到 length-1。超出范围会报 ArrayIndexOutOfBoundsException
  • 获取长度数组名.length——这是属性,不是方法,不加括号。
  • 遍历数组:普通 for 循环(按索引,可以修改元素)和增强 for 循环(for (类型 变量 : 数组),简洁但只读)。
  • 常见操作:求和、平均值、找最大最小值、查找指定元素。
  • 二维数组int[][] grid = new int[行数][列数];。用两个索引访问,用嵌套循环遍历。

数组和循环是编程中的黄金搭档——数组存数据,循环处理数据。掌握了这两者,你已经可以解决很多实际的计算和统计问题了。下一篇,我们进入 Java 最核心的编程思想——面向对象编程,学习类和对象。

下一篇预告

下一篇——《面向对象(上)——类与对象》:什么是类、什么是对象、怎样定义一个类、怎样创建对象、成员变量和成员方法、构造方法。this 关键字的作用。这是 Java 编程思想的核心,也是从“写脚本”到“写程序”的思维转变。

Java 零基础入门,每周更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容