Java 零基础教程

Java 多维数组

数组是存储相同类型数据集合的强大工具。在此之前,你主要接触的是一维数组,它们就像简单的列表或数据行。然而,许多现实场景需要表示具有多个维度的数据,例如表格、网格甚至更复杂的结构。

这就是多维数组发挥作用的地方。多维数组允许你按行和列(甚至更复杂的层级)组织数据,从而更轻松地建模和操作结构化信息。

1. 理解多维数组

多维数组本质上是数组的数组。最常见的多维数组是二维(2D)数组,你可以将其想象为一个带有行和列的网格或表格。

虽然 Java 在技术上并不直接支持真正的多维数组,但它允许你创建一个数组,其中的每个元素本身也是一个数组。这在效果上模拟出了多维结构。

想象一下电子表格:它有行和列,每个单元格保存一个数据。二维数组的工作方式与之类似,你需要指定行索引列索引来访问特定的数据。

2. 声明和初始化二维数组

要声明二维数组,你需要使用两组方括号 [],每一组代表一个维度。

2.1 声明语法

声明二维数组的基本语法如下:

数据类型[][] 数组名;

例如,声明一个整数类型的二维数组(矩阵):

int[][] matrix;

2.2 初始化数组大小

初始化二维数组时,你需要指定每个维度的长度。对于二维数组,这意味着要指定行数和列数。

// 声明并初始化一个 3 行 4 列的二维数组
int[][] matrix = new int[3][4];

这会创建一个名为 matrix 的二维数组,其中所有元素都被初始化为默认值(int 为 0,对象为 nullbooleanfalse 等)。

2.3 使用初始化列表

你也可以在声明的同时直接赋值,这与一维数组类似。每一组内部的大括号 {} 代表一行。

// 直接初始化二维数组
int[][] grades = {
    {90, 85, 92},  // 学生 1 的成绩(如:数学、科学、英语)
    {78, 88, 80},  // 学生 2 的成绩
    {95, 90, 87}   // 学生 3 的成绩
};

在这个例子中,grades 是一个 3 行 3 列的数组。每一行代表一个学生,每一列代表一个科目的成绩。

3. 交错数组 (Jagged Arrays)

Java “数组的数组”这种设计带来了一个强大的特性:交错数组(也称为不规则数组)。这意味着内部数组(即每一行)的长度可以不同。当你的数据行长短不一时,这种灵活性非常有用。

要创建交错数组,你在初始化外部数组时只指定行数,然后分别为每一行初始化不同的长度:

// 声明一个有 3 行的二维数组,列数稍后设置
int[][] irregularMatrix = new int[3][];

// 为每一行初始化不同的列数
irregularMatrix[0] = new int[5]; // 第一行有 5 列
irregularMatrix[1] = new int[2]; // 第二行有 2 列
irregularMatrix[2] = new int[4]; // 第三行有 4 列

这种灵活性是因为在 Java 中,二维数组实际上是一个存放引用的数组,每个引用指向另一个不同大小的一维数组。

4. 访问多维数组中的元素

要访问二维数组中的特定元素,你需要提供两个索引:行索引和列索引。和一维数组一样,索引是从 0 开始的。

语法如下:
数组名[行索引][列索引]

让我们以 grades 数组为例:

int[][] grades = {
    {90, 85, 92},
    {78, 88, 80},
    {95, 90, 87}
};

// 访问元素:
int firstStudentMathGrade = grades[0][0]; // 访问第 0 行第 0 列(结果为 90)
System.out.println("第一位学生的数学成绩: " + firstStudentMathGrade); 

int secondStudentScienceGrade = grades[1][1]; // 访问第 1 行第 1 列(结果为 88)
System.out.println("第二位学生的科学成绩: " + secondStudentScienceGrade);

// 修改元素:
grades[0][0] = 93; // 将第一位学生的数学成绩修改为 93
System.out.println("更新后的第一位学生数学成绩: " + grades[0][0]);

5. 理解二维数组的长度属性

在二维数组中,.length 属性的行为略有不同:

  • 数组名.length:返回行数(外部数组的长度)。
  • 数组名[行索引].length:返回该特定行中的列数(内部数组的长度)。

这在处理交错数组时特别有用。

int[][] exampleArray = {
    {1, 2, 3},
    {4, 5},
    {6, 7, 8, 9}
};

System.out.println("行数: " + exampleArray.length);             // 输出: 3
System.out.println("第 0 行的长度: " + exampleArray[0].length);    // 输出: 3
System.out.println("第 1 行的长度: " + exampleArray[1].length);    // 输出: 2
System.out.println("第 2 行的长度: " + exampleArray[2].length);    // 输出: 4

6. 实践案例演示

6.1 案例 1:存储简易游戏棋盘

想象一下创建一个井字棋(Tic-Tac-Toe)游戏。3x3 的网格是二维数组的完美应用场景。我们可以用字符来表示空位、'X' 和 'O'。

public class TicTacToeBoard {
    public static void main(String[] args) {
        // 声明并初始化一个 3x3 的字符数组作为井字棋盘
        char[][] board = {
            {' ', ' ', ' '}, // 第 0 行
            {' ', ' ', ' '}, // 第 1 行
            {' ', ' ', ' '}  // 第 2 行
        };

        // 进行几次落子
        board[0][0] = 'X'; // 玩家 X 在左上角落子
        board[1][1] = 'O'; // 玩家 O 在中心落子
        board[0][1] = 'X'; // 玩家 X 在上方中间落子

        // 显示棋盘当前状态
        System.out.println("当前井字棋盘状态:");
        // 遍历行
        for (int i = 0; i < board.length; i++) {
            // 遍历当前行中的列
            for (int j = 0; j < board[i].length; j++) {
                System.out.print(board[i][j]); // 打印元素
                if (j < board[i].length - 1) {
                    System.out.print(" | "); // 打印列分隔符
                }
            }
            System.out.println(); // 换行
            if (i < board.length - 1) {
                System.out.println("---------"); // 打印行分隔符
            }
        }
    }
}

6.2 案例 2:存储销售数据

假设你需要存储不同产品的月度销售数据。二维数组可以轻松胜任:行表示产品,列表示月份(或季度)。

public class SalesData {
    public static void main(String[] args) {
        // 3 种产品在 4 个季度的销售数据
        // 行:产品 (产品 A, 产品 B, 产品 C)
        // 列:季度 (Q1, Q2, Q3, Q4)
        double[][] quarterlySales = {
            {1500.75, 1800.50, 2100.20, 1950.00}, // 产品 A 销售额
            {1200.00, 1350.25, 1400.00, 1600.50}, // 产品 B 销售额
            {800.00,  950.00,  1100.75, 1250.00}  // 产品 C 销售额
        };

        // 计算产品 B 的总销售额
        double totalSalesProductB = 0;
        for (int i = 0; i < quarterlySales[1].length; i++) { // 遍历产品 B 行的每一列
            totalSalesProductB += quarterlySales[1][i];
        }
        System.out.println("产品 B 的总销售额: $" + totalSalesProductB);

        // 计算所有产品在第三季度 (Q3) 的总销售额
        double totalSalesQ3 = 0;
        for (int i = 0; i < quarterlySales.length; i++) { // 遍历每一行
            totalSalesQ3 += quarterlySales[i][2]; // Q3 的索引是 2
        }
        System.out.println("所有产品在 Q3 的总销售额: $" + totalSalesQ3);

        // 查找产品 A 的单季最高销售额
        double highestSalesProductA = quarterlySales[0][0];
        for (int i = 1; i < quarterlySales[0].length; i++) {
            if (quarterlySales[0][i] > highestSalesProductA) {
                highestSalesProductA = quarterlySales[0][i];
            }
        }
        System.out.println("产品 A 的最高季度销售额: $" + highestSalesProductA);
    }
}