首页 > 编程笔记 > Java笔记 阅读:4

Java二维数组的定义和使用(非常详细)

在 Java 程序中,一维数组主要用于存储线性数据,比如存储某校某年级所有学生一门课程的成绩,这种数据存储的结构是单个维度的。但是在实际应用中,一维数组并不能满足所有需求,比如存储某校某年级所有学生两门课程的成绩。所以,Java中提供了多维数组。

本质上讲,Java 并没有真正的多维数组结构,它的多维数组的本质是让数组元素再存储一个数组,从而构成多维数组的结构。

本节将以二维数组为例讲解多维数组的用法。二维数组的数据结构类似于一张表,包含行和列,下面展开详细讲解。

Java二维数组的创建

二维数组的结构可以看作是一张表,其中包含行和列,分别对应第一维度和第二维度。

下图是一个 3 行 2 列的二维数组的存储结构示意图。


图 1 3行2列二维数组结构

在 Java 中,声明二维数组需要使用两个中括号进行定义,在分配二维数组元素空间时,需要指明两个维度的数组长度。

二维数组的创建和一维数组类似,也需要进行数组声明和内存空间分配,创建二维数组的语法如下:
数据类型[][] 数组名;              // 声明二维数组变量
数组名 = new 数据类型[第一维长度][第二维长度];  // 为二维数组分配内存空间
当然也可以将数组声明和数组的内存分配用一行代码定义:
数据类型[][] 数组名 = new 数据类型[第一维长度][第二维长度];  // 声明二维数组并分配内存空间

根据二维数组的语法,创建一个 3 行 2 列的整型二维数组的示例代码如下:
int[][] scores;      // 声明二维数组变量scores
scores = new int[3][2];  // 为二维数组分配3行2列的内存空间
在上述示例代码中,第 1 行声明了一个整型二维数组变量 scores,这个变量未指向任何数组空间。接着,第 2 行代码先通过 new 运算符创建了一个 3 行 2 列的整型二维数组空间。然后,将二维数组赋值给 scores 变量,因此 scores 变量存储了整型二维数组的地址。通过 scores 变量可以访问二维数组对象空间的每个元素。

对于二维数组的声明和内存分配,如下图所示:


图 2 二维数组声明和内存分配

图 2(a)声明了一个整型二维数组变量 scores,因为没有指向任何数组对象,所以 scores 变量存储的内容是 null。

图 2(b)在堆内存中创建了一个 3 行 2 列的整型二维数组。图 2(b)中的 scores 变量存储了这个二维数组的首地址。二维数组的第一维是一个长度为 3 的一维数组,这个一维数组的每个空间都指向另外一个长度为 2 的一维数组,由此构成了二维数组结构。通过 scores 变量使用两个维度的下标可以访问到二维数组中的每个数组元素。

Java二维数组的内存分配

二维数组创建之后,就可以存储数据到其元素空间中。二维数组元素的赋值方式与一维数组类似,也是 3 种方式:静态初始化、动态初始化、通过数组下标为数组赋值。

1) 静态初始化

二维数组的静态初始化就是在声明数组时,由开发者显式指定二维数组元素的初始值。

因为二维数组有两个维度,所以在设置数据时,要使用两层大括号来体现两个维度的结构。如下是两种二维数组静态初始化的语法:
// 声明数组的同时初始化,外面的大括号表示第一维元素,里面的大括号表示第二维元素
数据类型[][] 数组名 = {{数据1,数据2,…},{数据1,数据2,…},…};
// 先声明二维数组,然后进行初始化
数据类型[][] 数组名;
数组名 = new 数据类型[][]{{数据1,数据2,…},{数据1,数据2,…},…};
上述语法中,二维数组静态初始化时,初始化的数据要写在两层大括号中,这是与一维数组不同的地方:
另外,在第 2 种静态初始化语法中,右边表达式中的“[ ][ ]”内,也不允许写数组长度,否则会发生语法错误。

二维数组静态初始化示例代码如下:
int[][] scores = {{78,65},{98,79},{87,89}};   // 声明数组并进行静态初始化
int[][] scores;                 // 先声明数组变量
scores = new int[][]{{78,65},{98,79},{87,89}};  // 再进行静态初始化

2) 动态初始化

二维数组进行动态初始化时,开发者需要指定两个维度的数组长度,然后由系统自动为数组元素分配初始值。二维数组动态初始化的语法格式如下:
数据类型[][] 数组名 = new 数据类型[第一维数组长度][第二维数组长度];
二维数组在进行动态初始化后,程序会根据指定的两个维度的数组长度,创建对应的数组元素空间,并为每个数组元素空间设置初始值。

二维数组动态初始化的代码示例如下:
int[][] scores = new int[3][2];     // 创建3行2列的整型二维数组,元素的初始值都是0
String[][] names = new String[3][2];  // 创建3行2列的字符串二维数组,元素的初始值都为null

3) 通过数组下标为数组赋值

在二维数组创建之后,可以使用数组名结合二维数组行列下标的方式,为二维数组空间中的每个元素赋值。

使用二维数组下标赋值的语法格式如下:
数据类型[][] 数组名 = new 数据类型[第一维数组长度][第二维数组长度];
数组名[第一维度下标][第二维度下标] = 数值;
在通过数组下标为数组元素赋值时,每个维度的数组下标的取值范围从0到对应维度的数组长度减1为止。下标超出这个范围,会发生“ArrayIndexOutOfBoundsException”数组下标越界的异常。

通过数组下标为元素赋值的示例代码如下:
String[][] names = new String[3][2];  // 声明3行2列字符串二维数组
names[0][0] = "张三";         // 通过2个维度的下标为每个数组元素赋值
names[0][1] = "李四";
names[1][0] = "王五";
names[1][1] = "赵六";
names[2][0] = "孙七";
names[2][1] = "钱八";

Java嵌套循环存取二维数组

二维数组有两个维度,在访问数组元素时要通过两个下标来访问。因为数组的下标具有连续性的特点,所以可以通过循环嵌套的方式来访问二维数组的每个元素。

接下来,通过循环嵌套的方式为一个 3 行 2 列的二维数组赋值,并用循环嵌套的方式输出存入的数据:
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);    // 定义输入扫描器对象,用于接收键盘输入
        // 定义3行2列的二维数组,存储3名学生的2门成绩
        int[][] scores = new int[3][2];
        // 使用嵌套循环的方式为数组赋值,scores.length获取第一维数组的长度
        for (int i = 0; i < scores.length; i++) {
            // scores[i].length获取第二维数组的长度
            for (int j = 0; j < scores[i].length; j++) {
                System.out.println("请输入第" + (i + 1) + "个学生的第" + (j + 1) + "门课成绩:");
                scores[i][j] = sc.nextInt();    // 将输入的数据存入对应的数组位置
            }
        }
        System.out.println("==========");    // 输出分隔符

        // 使用嵌套循环的方式打印二维数组元素,scores.length获取第一维数组的长度
        for (int i = 0; i < scores.length; i++) {
            // scores[i].length获取第二维数组的长度
            for (int j = 0; j < scores[i].length; j++) {
                int score = scores[i][j];    // 获取当前遍历到的二维数组元素
                // 输出分数
                System.out.println("第" + (i + 1) + "个学生的第" + (j + 1) + "门课成绩是:" + score);
            }
        }
    }
}
程序的运行结果如下:
请输入第1个学生的第1门课成绩:
23
请输入第1个学生的第2门课成绩:
34
请输入第2个学生的第1门课成绩:
45
请输入第2个学生的第2门课成绩:
56
请输入第3个学生的第1门课成绩:
67
请输入第3个学生的第2门课成绩:
87
====================
第1个学生的第1门课成绩是:23
第1个学生的第2门课成绩是:34
第2个学生的第1门课成绩是:45
第2个学生的第2门课成绩是:56
第3个学生的第1门课成绩是:67
第3个学生的第2门课成绩是:87
程序中先定义了一个输入扫描器对象(Scanner),用于接收从键盘输入的数据。接着,定义了一个 3 行 2 列的整型二维数组,用于存储 3 个学生两门课程的成绩。然后使用嵌套循环实现二维数组元素的赋值。

scores.length 是第 1 层循环的终止条件,它表示的是数组第一维度数组的长度。scores[i].length 是第 2 层循环的终止条件,它表示的是数组第二维度的长度。

在第 2 层循环内部,使用 scores[i][j] 表示访问到的某个二维数组元素,用于存储用户通过键盘输入的数据。嵌套循环结束后,这个二维数组的所有元素都会被赋值。接下来,通过另外一个嵌套循环,将二维数组内的所有元素逐一打印输出。

Java 中的多维数组本质上是在数组中存储数组,是在一维数组的基础上衍生出来的,因此理论上可以定义任何维度的数组。定义二维数组时需要使用两个中括号“[ ][ ]”,以此类推,定义三维、四维数组只要定义对应个数的中括号即可。

Java非对称型数组

前面讲的二维数组用的都是矩形数组,也就是数组的第二维度的长度都是一样的,是等行等列的对称结构。

Java中还有一种非对称的数组结构,被称为非对称型数组。对称型数组和非对称型数组的结构如下图所示。


图 3 对称型数组和非对称型数组

对称型的 3 行 4 列数组有 12 个元素,但是非对称型数组的元素个数是不确定的。这里,我们定义一个非对称型数组,并进行静态初始化,然后输出第一维元素指向的数组中的最后一个元素,示例代码如下:
int[][] scores = {{1,2,3},{5,6,7,8},{9,10}};   // 用静态初始化的方式定义非对称型数组
System.out.println(scores[0][2]);        // 输出第一维下标为0的数组的最后一个元素
System.out.println(scores[1][3]);        // 输出第一维下标为1的数组的最后一个元素
System.out.println(scores[2][1]);        // 输出第一维下标为2的数组的最后一个元素
在上述示例代码声明的二维数组中,第一维长度是 3。但是,第二维长度各不相同,长度分别是 3、4、2。因此,第一维元素关联的数组的最后一个元素的下标是各不相同的。

相关文章