Java二维数组的定义和使用(非常详细)
在 Java 程序中,一维数组主要用于存储线性数据,比如存储某校某年级所有学生一门课程的成绩,这种数据存储的结构是单个维度的。但是在实际应用中,一维数组并不能满足所有需求,比如存储某校某年级所有学生两门课程的成绩。所以,Java中提供了多维数组。
本质上讲,Java 并没有真正的多维数组结构,它的多维数组的本质是让数组元素再存储一个数组,从而构成多维数组的结构。
本节将以二维数组为例讲解多维数组的用法。二维数组的数据结构类似于一张表,包含行和列,下面展开详细讲解。
下图是一个 3 行 2 列的二维数组的存储结构示意图。

图 1 3行2列二维数组结构
在 Java 中,声明二维数组需要使用两个中括号进行定义,在分配二维数组元素空间时,需要指明两个维度的数组长度。
二维数组的创建和一维数组类似,也需要进行数组声明和内存空间分配,创建二维数组的语法如下:
根据二维数组的语法,创建一个 3 行 2 列的整型二维数组的示例代码如下:
对于二维数组的声明和内存分配,如下图所示:

图 2 二维数组声明和内存分配
图 2(a)声明了一个整型二维数组变量 scores,因为没有指向任何数组对象,所以 scores 变量存储的内容是 null。
图 2(b)在堆内存中创建了一个 3 行 2 列的整型二维数组。图 2(b)中的 scores 变量存储了这个二维数组的首地址。二维数组的第一维是一个长度为 3 的一维数组,这个一维数组的每个空间都指向另外一个长度为 2 的一维数组,由此构成了二维数组结构。通过 scores 变量使用两个维度的下标可以访问到二维数组中的每个数组元素。
因为二维数组有两个维度,所以在设置数据时,要使用两层大括号来体现两个维度的结构。如下是两种二维数组静态初始化的语法:
另外,在第 2 种静态初始化语法中,右边表达式中的“[ ][ ]”内,也不允许写数组长度,否则会发生语法错误。
二维数组静态初始化示例代码如下:
二维数组动态初始化的代码示例如下:
使用二维数组下标赋值的语法格式如下:
通过数组下标为元素赋值的示例代码如下:
接下来,通过循环嵌套的方式为一个 3 行 2 列的二维数组赋值,并用循环嵌套的方式输出存入的数据:
scores.length 是第 1 层循环的终止条件,它表示的是数组第一维度数组的长度。scores[i].length 是第 2 层循环的终止条件,它表示的是数组第二维度的长度。
在第 2 层循环内部,使用 scores[i][j] 表示访问到的某个二维数组元素,用于存储用户通过键盘输入的数据。嵌套循环结束后,这个二维数组的所有元素都会被赋值。接下来,通过另外一个嵌套循环,将二维数组内的所有元素逐一打印输出。
Java 中的多维数组本质上是在数组中存储数组,是在一维数组的基础上衍生出来的,因此理论上可以定义任何维度的数组。定义二维数组时需要使用两个中括号“[ ][ ]”,以此类推,定义三维、四维数组只要定义对应个数的中括号即可。
Java中还有一种非对称的数组结构,被称为非对称型数组。对称型数组和非对称型数组的结构如下图所示。

图 3 对称型数组和非对称型数组
对称型的 3 行 4 列数组有 12 个元素,但是非对称型数组的元素个数是不确定的。这里,我们定义一个非对称型数组,并进行静态初始化,然后输出第一维元素指向的数组中的最后一个元素,示例代码如下:
本质上讲,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,…},…};上述语法中,二维数组静态初始化时,初始化的数据要写在两层大括号中,这是与一维数组不同的地方:
- 第 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。因此,第一维元素关联的数组的最后一个元素的下标是各不相同的。