13. 多维数组¶
13.1. 数组中的维数¶
到目前为止,当我们使用 List 或数组时,我们处理的都是所谓的 一维数组 。
也就是说,我们一直在处理一行数值。
但是,如果我们想创建一个由数组组成的数组呢?
二维数组 是一种其元素本身也是数组的数组,对于某些类型的问题,它是必要的或者有用的。 例如,如果你正在进行一项科学研究,需要记录一年中每一天的降水量,你就会用到这种类型的数组。
组织这些数据的一种方式是创建一个由 365 个元素组成的一维数组:
double[] rainfall = new double [ 365 ];
然而,使用这种表示方式,要计算某一个月的平均降水量会非常困难, 而这可能是你研究中的一个重要部分。
对于这个问题,更好的表示方法是使用二维数组,其中一个维度表示月份,另一个维度表示天数。
下面的语句声明了数组变量 rainfall ,并创建一个 12×31 的数组对象作为它的引用:
double[] rainfall = new double[12][31];
因此, rainfall 是一个数组的数组。你可以把第一个数组看成这个问题的 12 个月份。
你也可以把每个月看成包含 31 天的数组。月份的下标从 0 到 11,天数的下标从 0 到 30。
这种表示方式的问题在于,当我们想引用 1 月 5 日的降水量时,不得不使用 rainfall[0][4] 。
这既别扭又容易引起误解。
问题在于日期(如 12/31/2021)从 1 开始计数,而数组从 0 开始计数。
由于这一点不容易记住,我们这种降水数据的表示方式可能会让我们在开始编写算法时出错。 我们只需把数组定义成多一个月、每个月多一天,就可以轻松解决这个问题:
double[] rainfall = new double[13][32];
这种表示方式创建了一个包含 13 个月(下标从 0 到 12)、每月 32 天(下标从 0 到 31)的数组。不过,我们可以在处理该数组的所有算法中采用从 1 开始的索引,从而简单地忽略第 0 个月和第 0 天。换句话说,如果我们把这个数组看成一张由 13 行 32 列组成的二维表,就可以让第 0 行和第 0 列保持空闲。
正如上图所示,这个 416 个元素数组的第一个元素的子脚本是 (0,0) ,而最后一个位置的下标是 (12,31) 。 这种表示方式的主要优点是,整个程序将更容易阅读和理解,也更不容易出错。
为了访问二维数组中的一个元素,您需要使用两个下标。对于数组 rainfall ,第一个下标将指定月份,第二个将指定月份内的日期。因此,以下语句将 1.15 分配给代表 1 月 5 日的 rainfall 元素,然后打印其值:
double[] rainfall = new double[13][32];
rainfall[1][5] = 1.15; // rainfall for January 1st is 1.15
与一维数组的情况一样,试图引用数组中不存在的元素是一种错误。下面的每个示例都会让 Java 抛出 IndexOutOfBoundsException :
double[] rainfall = new double[13][32];
rainfall[13][32] = 1.15; // no such element
rainfall[11][33] = 1.15; // no such column
rainfall[14][2] = 1.15; // no such row
13.1.1. 遍历二维数组¶
正如我们之前提到的,一个 double 数组会自动把所有值初始化为 0,所以我们不需要初始化元素。 记住,如果我们处理的是字符串或对象,情况就不是这样了!
然而,对于许多数组问题而言,有必要把数组元素初始化成其他值。对于二维数组,这需要用到嵌套循环。
为了说明这个算法,让我们用一个嵌套 for 循环把 rainfall 数组的每个元素都初始化成 0:
for (int month = 1; month < rainfall.length ; month++)
{
for (int day = 1 ; day < rainfall [month ].length ; day++)
{
rainfall [month][day] = 0.0 ;
}
}
注意两个 for 循环都从 1 开始,因为我们不使用第 0 行或第 0 列。
记住,当你有嵌套 for 循环时,内层循环迭代得更快。因此,对每个月份而言,内层循环将遍历 31 天。这相当于把数组看作上一节的图中所展示的形式,先水平地处理每一行,再向下处理下一行。
注意,二维数组的两个维度都有关联的 length 变量,本例中使用这个变量来指定每个 for 循环的上界。
对于 rainfall 数组,第一个维度(月份)的长度是 13,第二个维度(天数)的长度是 32。
另一种理解 rainfall 数组的方式是记住它本身是一个数组的数组。
第一个数组的长度,即月份个数(13),由 rainfall.length 给出。每个月数组的长度,即该月的天数(32),由 rainfall[month].length 给出。
嵌套 for 循环的外层循环遍历月份 1 到 12,内层 for 循环遍历天数 1 到 31。这样一来,数组中共有 372 = 12 × 31 个元素被设置为 0.0。
