
3.4 数组
前面程序中使用的变量均为基本数据类型的变量,各个变量之间没有任何联系。有一些变量,例如s1,s2,…,s10,它们可以代表同一个班中10个学生的成绩,这些变量都用相同的名字,只是下角标有所区别,将这种元素组成的一组变量称为数组。
在Java语言中,数组是一种最简单的复合数据类型(引用数据类型)。数组提供了一种将有关联的数据分组的方法。数组的最主要的特性:①数组是有序数据的集合;②数组中的每个元素都具有相同的数据类型;③所有元素具有相同的名字。用数组名和下标可以唯一地确定数组中的元素。
数组有一维数组、二维数组和多维数组。下面先来介绍一维数组。
Java语言中的变量的下标用方括号括起来,即s[1],s[2],…,s[10],这就是数组类型变量。
注意:数组的有序性是指数组元素存储的有序性,而不是指数组的元素值有序。利用这种有序性,可以方便地用指针解决一些问题。
3.4.1 一维数组
一维数组中各个数组元素是排成一行的一组下标变量,用一个统一的数组名来标识,用一个下标来标明其在数组中的位置(下标从0开始)。一维数组通常和一重循环配合使用来实现对数组元素进行的处理。
1. 一维数组的声明
要使用一个数组,必须首先声明数组。通用的一维数组的声明格式如下:
数组元素类型 数组名[];
或
数组元素类型[]数组名;
例如:

说明:
(1)数组名:命名原则遵循标识符的命名规则。本例中a就是数组名。
(2)上面第二种数组声明格式其中的方括号紧跟在类型标识符的后面,而不是跟在数组变量名的后面,两者在用法上没有区别。例如,下面的两个定义是等价的。

(3)推荐使用“数组元素类型[]数组名;”格式,这种格式比较而言有更好的可读性,目前越来越多的语言不再支持“数组元素类型 数组名[];”的格式。
注意:定义数组时不能指定数组长度。数组是一种引用类型的变量,在定义变量时只是定义了一个指针,还没有指向任何有效内存,因此定义数组时不能指定数组长度。初始化数组时可以指定数组长度。
2. 一维数组的创建
和其他语言不同,在Java语言中,尽管在上面声明了x是一个整型数组,但实际上还没有数组变量存在。为了使x数组成为物理上存在的整型数组,必须用运算符new来为其分配地址并且把它赋给x。运算符new是专门用来分配内存的运算符,称这个过程为创建数组。
数组创建的一般格式如下:

其中,数组元素类型指定被分配的数据类型,size指定数组中变量的个数即长度,数组名是被引用到数组的数组变量。也就是说,使用运算符new来分配数组,必须指定数组元素的类型和数组元素的个数。用运算符new分配数组后,数组中的元素将会被自动初始化为默认值。另外,数组中的元素个数size(长度)是不能改变的,这和后面要介绍的集合是有区别的。例如:

通过上面这个语句的执行,数组x将会指向10个整数,而且数组中的所有元素将被初始化为0。
这里创建了一个一维数组a,如图3-15所示。该数组由10个数组元素构成,其中每一个数组元素都属于整型数据类型,在内存中分配10个占整型单元的连续存储空间,并将数组的首地址送给a。数组a的各个数据元素依次是a[0],a[1],a[2],…,a[9](注意,下标从0~9)。每个数组元素将被初始化为0。

图3-15 一维数组
在实际应用中,经常将数组声明和数组创建的两条语句合为一个语句。例如,将下面两条语句

可以合并为下面一条语句。

3. 一维数组的初始化
上面介绍了使用运算符new来为数组所要存储的数据分配内存,并把它们分配给数组变量。这时数组中的元素将会被自动初始化为默认值。例如,一般类型数组元素的默认初始值如下:
整型:0。
实型:0.0f或0.0d。
字符型:'\0'。
类对象:null。
布尔类型:false。
数组的初始化工作非常重要,不能使用任何未初始化的数组。一旦分配了一个数组,就可以通过在方括号内指定它的下标来访问数组中特定的元素,并且为它赋初值。所有的数组下标都从0开始。例如:

表示a[0]=1,a[1]=2,a[2]=3,a[3]=4,a[4]=5。
再如,下面的语句将值2赋给数组a的第二个元素。

4. 一维数组的引用
数组在定义之后,就可以在程序中引用其数组元素。数组元素的引用形式如下:
数组名[下标]
说明:引用数组元素时,下标可以是整型常数、已经赋值的整型变量或整型表达式。

相当于

注意:引用数组元素时,下标不能越界。Java的运行系统会检查以确保所有的数组下标都在正确的范围以内。如果企图访问数组边界以外(如负数或者比数组边界大)的元素,则将引起运行错误。
若有定义:
int[]a=new int[5];
则数组a的元素分别为:a[0]、a[1]、a[2]、a[3]、a[4],但a[5]不是数组a的元素。
每个元素都可作为一个整型变量来使用,例如:
a[0]=5; a[3]=a[1]+4; a['D'-'B']=3;
3.4.2 一维数组的应用
数组的应用非常广泛。使用数组,通常依据数组下标和边界的概念,结合前面讲过的for循环。下面将介绍数组应用的几个例子。
1. 运用一维数组来计算一组数字的和
【例3-24】 数组的使用。

程序运行结果:

注意:在JDK5.0以后的版本中,使用增强型for…each循环遍历一维数组将更加容易。例如下面的程序:

程序运行结果:

【例3-25】 计算10个学生的平均成绩。
算法分析:
(1)定义一个有10个元素的一维数组用来存放学生的成绩。
(2)将成绩进行累加,并计算平均成绩。
程序如下:

程序运行结果:

2. 利用数组求Fibonacci数列的前n项
【例3-26】 求Fibonacci数列前20个数。
这是由一个古老的数学问题产生的序列:1,1,2,3,5,8,13,…,可以归结为以下数学公式:
F1=1 (n=1)
F2=1 (n=2)
Fn=Fn-1+Fn-2 (n≥3)
从公式中可以看出:数列的组成是有规律的,数列的前两项都是1,从第三项开始,每个数据项的值为前两个数据项的和,采用递推方法来实现。可以用一个一维整型数组f[20]来保存这个数列的前20项。
程序如下:

程序运行结果:

3. 利用数组实现数据排序
在实际应用中,数据的排序是一种常用的数据组织方法。这里介绍冒泡排序方法。
【例3-27】 采用“冒泡法”对10个整数按从小到大的顺序排序。
算法分析:冒泡法排序思路是将相邻的两个数比较,将小的调到前头。
任意n个数排序过程如下:
(1)比较第一个数与第二个数,若为逆序a[1]>a[2],则交换;然后比较第二个数与第三个数;以此类推,直至第n-1个数和第n个数比较为止,这样完成第一趟冒泡排序,结果最大的数被安置在最后一个元素位置上。
(2)对前n-1个数进行第二趟冒泡排序,结果使次大的数被安置在第n-1个元素位置上。
(3)重复上述过程,共经过n-1趟冒泡排序后,即n个数已从小到大排序,则排序结束。
以1~5这5个数为例,排序过程示例如下:
起始状态: [5 2 3 1 4]
第1趟排序后:[2 3 1 4] 5
第2趟排序后:[2 1 3] 4 5
第3趟排序后:[1 2] 3 4 5
第4趟排序后:[1] 2 3 4 5
从这里可以看出,1~5这5个数经过4趟排序就排好序了。
10个整数排序程序如下:

程序运行结果:

3.4.3 二维数组
1. 二维数组的声明
二维数组的声明格式如下:
数组元素类型 数组名[][];
或
数组元素类型[][] 数组名;
例如:

或
float[][] arrayName;
2. 二维数组的创建和初始化
1)静态创建和初始化
例如:

在Java语言中,由于把二维数组看作数组的数组,数组空间不是连续分配的,所以不要求二维数组每一维的大小相同。
2)动态创建和初始化
(1)直接为每一维分配空间,其格式如下:

二维数组的数组元素可以看作是排列为行列的形式(矩阵)。二维数组元素也用统一的数组名和下标来标识,第一个下标表示行,第二个下标表示列。每一个下标从0开始。

图3-16 二维数组
上面的例子定义了一个二维数组b,该数组由6个元素构成,如图3-16所示。其中,每一个数组元素都属于浮点(实数)数据类型。数组b的各个数据元素依次是:
b[0][0],b[0][1],b[0][2],b[1][0],b[1][1],b[1][2]
说明:
- 二维数组中的每个数组元素都有两个下标,且必须分别放在单独的[]内。
- 二维数组定义的第一个下标表示该数组具有的行数,第二个下标表示该数组具有的列数,两个下标之积是该数组具有的数组元素的个数。
- 二维数组中的每个数组元素的数据类型均相同。二维数组的存放规律是“按行排列”。
- 二维数组可以看作数组元素为一维数组的数组。例如,上面的例子可以看作特殊的一维数组,即b[0],b[1]。
- 真正意义来说,Java没有多维数组,多维数组都是由一维数组构成的。
(2)从最高维开始,分别为每一维分配空间。
例如,二维基本数据类型数组的动态初始化如下:

对二维复合数据类型的数组,必须首先为最高维分配引用空间,然后再顺次为低维分配空间,而且必须为每个数组元素单独分配空间。例如:

当给多维数组分配内存时,只要指定第一个(最左边)维数的内存即可。可以单独地给余下的维数分配内存。例如,下面的程序在数组twoDArray被定义时给它的第一维分配内存,对第二维则是手工分配地址。

尽管在这种情形下单独地给第二维分配内存没有什么优点,但在其他情形下就不同了。例如,当手工分配内存时,不需要给每个维数相同数量的元素分配内存。如前面所说,既然多维数组实际上是数组的数组,那么每个数组的维数均在控制之下。
例如,下列程序定义了一个二维数组,它的第二维的大小是不相等的。

对于大多数应用程序,不推荐使用不规则多维数组,因为它们的运行与人们期望的相反。但是,不规则多维数组在某些情况下使用效率较高。例如,如果需要一个很大的二维数组,而它仅仅被不规则地占用(即其中一维的元素不是全被使用),这时不规则数组可能是一个完美的解决方案。
3. 二维数组元素的引用
二维数组的操作一般由二重for循环(行循环,列循环)来完成。
例如,下面例题通过使用二维数组挑选出数组中最小的数值。
【例3-28】 二维数组的使用。

程序运行结果:

编程技巧:对二维数组的输入输出多使用二层循环结构来实现。外层循环处理各行,循环控制变量j作为数组元素的第一维下标;内层循环处理一行的各列元素,循环控制变量k作为元素的第二维下标。
【例3-29】 矩阵填数,生成如图3-17所示的矩阵并输出。

图3-17 例3-29矩阵
算法分析:观察上面的矩阵,可以分为4个部分,左上角元素全为1,右下角元素全为2,其余元素均为0。假设i和j分别表示数组的行列下标,则左上角元素满足条件:i<5&&j<5;右下角元素满足条件:i>=5&&j>=5。按照这种规律,可以用一个二维数组存储生成的数据,再将其输出。
程序如下:

注意:数组中的数据为生成的数据,无须输入。
3.4.4 多维数组
当数组元素的下标在两个或两个以上时,该数组称为多维数组。其中,二维数组最为常用。
多维数组定义格式如下:
类型说明[整型常数1][整型常数2]…[整型常数k]数组名;
例如:

定义了一个三维数组a,其中每个数组元素为整型,总共有2×2×3=12个元素,如图3-18所示。

图3-18 多维数组示例
对于三维数组,整型常数1、整型常数2、整型常数3可以分别看作“深”维(或“页”维)、“行”维、“列”维。可以将三维数组看作一个元素为二维数组的一维数组。三维数组在内存中先按页、再按行、最后按列存放。
多维数组在三维空间中不能用形象的图形表示。多维数组在内存中排列顺序的规律是:第一维的下标变化最慢,最右边的下标变化最快。
多维数组的数组元素的引用形式如下:
数组名[下标1][下标2]…[下标k]
在数组定义时,多维数组的维从左到右第一个[]称为第一维,第二个[]称为第二维,以此类推。多维数组元素的顺序仍由下标决定。下标的变化是先变最右边的,再依次变化左边的下标。
三维数组a的12个元素如下:

多维数组的数组元素可以在任何相同类型变量可以使用的位置引用,只是同样要注意不要越界。