4.6.4 没有多维数组

4.6.4 没有多维数组

Java语言里提供了支持多维数组的语法。但本书还是想说,没有多维数组一一如果从数组底层的运行机制上来看

Java语言里的数组类型是引用类型,因此数组变量其实是一个引用,这个引用指向真实的数组内存。数组元素的类型也可以是引用,如果数组元素的引用再次指向真实的数组内存,这种情形看上去很像多维数组。
回到前面定义数组类型的语法:type[] ArrName;,这是典型的一维数组的定义语法,其中type是数组元素的类型。如果希望数组元素也是一个引用,而且是指向int数组的引用,则可以把type具体成int[],那么上面定义数组的语法就是int[][] ArrName.

二维数组语法

如果把int这个类型扩大到Java的所有类型(不包括数组类型),则出现了定义二维数组的语法:

1
type[][] arrName;

Java语言采用上面的语法格式来定义二维数组,但它的实质还是一维数组,只是其数组元素也是引用,数组元素里保存的引用指向一维数组。
接着对这个“二维数组”执行初始化,同样可以把二维数组当成一维数组来初始化,把这个“二维数组”当成一个一维数组,其元素的类型是type[]类型,则可以采用如下语法进行初始化

1
arrName = new type [length][];

上面的初始化语法相当于初始化了一个一维数组,这个一维数组的长度是length。同样,因为这个一维数组的数组元素是引用类型(数组类型)的,所以系统为每个数组元素都分配初始值:null

这个二维数组实际上完全可以当成一维数组使用:使用new Type[length]初始化一维数组后,相当于定义了lengthtype类型的变量;类似的,使用new Type[length][]初始化这个数组后,相当于定义了lengthtype[]类型的变量,当然,这些type[]类型的变量都是数组类型,因此必须再次初始化这些数组。

程序示例 以一维数组的方式处理二维数组

下面程序示范了如何把二维数组当成一维数组处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 定义一个二维数组
int[][] a;
// 把a当成一维数组进行初始化,初始化a是一个长度为4的数组
// a数组的数组元素又是引用类型
a = new int[4][];
// 把a数组当成一维数组,遍历a数组的每个数组元素
for (int i = 0, len = a.length; i < len; i++) {
System.out.println(a[i]);
}
// 初始化a数组的第一个元素
a[0] = new int[2];
// 访问a数组的第一个元素所指数组的第二个元素
a[0][1] = 6;
// a数组的第一个元素是一个一维数组,遍历这个一维数组
for (int i = 0, len = a[0].length; i < len; i++) {
System.out.println(a[0][i]);
}

上面程序把a这个二维数组当成一维数组处理,只是每个数组元素都是null所看到输出结果都是null下面结合示意图来说明这个程序的执行过程。

程序的第一行int[][] a;,将在栈内存中定义一个引用变量,这个变量并未指向任何有效的内存空间,此时的堆内存中还未为这行代码分配任何存储区.

程序对a数组执行初始化:a = new int[4][];,这行代码让a变量指向一块长度为4的数组内存,这个长度为4的数组里每个数组元素都是引用类型(数组类型),系统为这些数组元素分配默认的初始值:null此时a数组在内存中的存储示意图如图4.12所示:

这里有一张图片

从图4.12来看,虽然声明a是一个二维数组,但这里丝毫看不出它是一个二维数组的样子,完全是一维数组的样子。这个一维数组的长度是4,只是这4个数组元素都是引用类型,它们的默认值是null,所以程序中可以把a数组当成一维数组处理,依次遍历a数组的每个元素,将看到每个数组元素的值都是null

由于a数组的元素必须是int[]数组,所以接下来的程序对a[0]元素执行初始化也就是让图4.12右边堆内存中的第一个数组元素指向一个有效的数组内存,指向一个长度为2的int数组。因为程序采用动态初始化a[0]数组,因此系统将为a[0]所引用数组的每个元素分配默认的初始值:0,然后程序显式为a[0]数组的第二个元素赋值为6。此时在内存中的存储示意图如图4.13所示:

这里有一张图片

图4.13中灰色覆盖的数组元素就是程序显式指定的数组元素值。接着迭代输出a[0]数组的每个数组元素,将看到输出0和6。

初始化多维数组时 可以只指定最左边维度的大小

从上面程序中可以看出,初始化多维数组时,可以只指定最左边维的大小;

初始化多维数组时 可以同时指定所有维度的大小

当然,也可以一次指定每一维的大小。例如下面代码:

1
2
// 同时初始化二维数组的2个维数
int[][] b = new int[3][4];

上面代码将定义一个b数组变量,这个数组变量指向一个长度为3的数组,这个数组的每个数组元素又是一个数组类型,它们各指向对应的长度为4的int[]数组,每个数组元素的值为0。这行代码执行后在内存中的存储示意图如图4.14所示
这里有一张图片

使用静态初始化

还可以使用静态初始化方式来初始化二维数组。使用静态初始化方式来初始化二维数组时,二维数组的每个数组元素都是一维数组,因此必须指定多个一维数组作为二维数组的初始化值。如下代码所示

1
2
3
4
5
6
// 使用静态初始化的语法来初始化一个二维数组
String[][] str1 = new String[][] { new String[3], new String[] { "hello" } };
// 使用简化的静态初始化语法来初始化二维数组
String[][] str2 = { new String[3], new String[] { "hello" } };
System.out.println(str1[1][0]);
System.out.println(str2[1][0]);

上面代码执行后内存中的存储示意图如图4.15所示
这里有一张图片
通过上面讲解可以得到一个结论:

  • 二维数组是一维数组,其数组元素是一维数组;
  • 三维数组也是一维数组,其数组元素是二维数组

从这个角度来看,Java语言里没有多维数组