达者为先 师者之意
Java 数组
在某些情况下,虽然可以使用单个变量来存储信息,但是如果需要存储的信息较多(例如存储 50 名学生的成绩),这时再依次创建变量声明并赋值显得非常麻烦。
随着处理的信息量越来越大,工作也就越来越烦琐,这时可以使用数组或集合来存储信息。通过使用数组,可以在很大程度上缩短和简化程序代码,从而提高应用程序的效率。
数组(array)是一种最简单的复合数据类型,它是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来确定数组中唯一的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等。
在 Java 中数组的下标是从零开始的,很多计算机语言的数组下标也从零开始。Java 数组下标访问运算符是中括号,如 intArray[0],表示访问 intArray 数组的第一个元素,0 是第一个元素的下标。Java 中的数组本身是引用数据类型,它的长度属性是 length。
1 一维数组
当数组中每个元素都只带有一个下标时,这种数组就是“一维数组”。一维数组(one-dimensional array)实质上是一组相同类型数据的线性集合,是数组中最简单的一种数组。
数组是引用数据类型,引用数据类型在使用之前一定要做两件事情:声明和初始化。所以本文将重点介绍一维数组的创建、初始化和使用。
1.1 创建一维数组
为了在程序中使用一个数组,必须声明一个引用该数组的变量,并指明整个变量可以引用的数组类型。声明一维数组的语法格式为:
type[] arrayName; // 数据类型[] 数组名;
或者
type arrayName[]; // 数据类型 数组名[];
可见数组的声明有两种形式:一种是中括号”[]“跟在元素数据类型之后,另一种是中括号”[]“跟在变量名之后。
对于以上两种语法格式而言,Java 更推荐采用第一种声明格式,因为第一种格式不仅具有更好的语意,而且具有更好的可读性。对于第一种格式type[] arrayName,很容易理解这是定义一个变量,其中变量名是 arrayName,而变量类型是 type[]。
前面已经指出:type[] 确实是一种新类型,与 type 类型完全不同(例如 int 类型是基本类型,但 int[] 是引用类型)。因此,这种方式既容易理解,也符合定义变量的语法。但第二种格式type arrayName[]的可读性就差了,看起来好像定义了一个类型为 type 的变量,而变量名是 arrayName[],这与真实的含义相去甚远。
可能有些读者非常喜欢type arrayName[]这种定义数组的方式,这可能是因为早期某些计算机读物的误导,从现在开始最好就不要再使用这种糟糕的方式了。
提示:Java 的模仿者 C# 就不再支持type arrayName[]这种语法,它只支持第一种定义数组的语法。越来越多的语言不再支持type arrayName[]这种数组定义语法。
以上两种格式都可以声明一个数组,其中的数据类型既可以是基本数据类型,也可以是引用数据类型。数组名可以是任意合法的变量名。声明数组就是要告诉计算机该数组中数据的类型是什么。例如:
int[] score; // 存储学生的成绩,类型为整型
double[] price; // 存储商品的价格,类型为浮点型
String[] name; // 存储商品名称,类型为字符串型
在声明数组时不需要规定数组的长度,例如:
int score[10]; // 这是错误的
注意:在声明数组变量时千万不要漏写[]。
1.2 分配空间
声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储。
简单地说,分配空间就是要告诉计算机在内存中为它分配几个连续的位置来存储数据。在 Java 中可以使用 new 关键字来给数组分配空间。分配空间的语法格式如下:
arrayName = new type[size]; // 数组名 = new 数据类型[数组长度];
其中,数组长度就是数组中能存放的元素个数,显然应该为大于 0 的整数,例如:
score = new int[10];
price = new double[30];
name = new String[20];
这里的 score 是已经声明过的 int[] 类型的变量,当然也可以在声明数组时就给它分配空间,语法格式如下:
type[] arrayName = new type[size]; // 数据类型[] 数组名 = new 数据类型[数组长度];
例如,声明并分配一个长度为 5 的 int 类型数组 arr,代码如下:
int[] arr = new int[5];[数组长度];
执行后 arr 数组在内存中的格式如下图所示。
一维数组的内存格式
在图 中 arr 为数组名称,方括号“[]”中的值为数组的下标。数组通过下标来区分数组中不同的元素,并且下标是从 0 开始的。因此这里包含 5 个元素的 arr 数组最大下标为 4。
注意:一旦声明了数组的大小,就不能再修改。这里的数组长度也是必需的,不能少。
尽管数组可以存储一组基本数据类型的元素,但是数组整体属于引用数据类型。当声明一个数组变量时,其实是创建了一个类型为“数据类型[]”(如 int[]、double[]、String[])的数组对象,它具有下表所示的方法和属性。
1.3 初始化一维数组
Java 语言中数组必须先初始化,然后才可以使用。所谓初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值。
能不能只分配内存空间,不赋初始值呢?
不行,一旦为数组的每个数组元素分配了内存空间,每个内存空间里存储的内容就是该数组元素的值,即使这个内存空间存储的内容为空,这个空也是一个值(null)。不管以哪种方式来初始化数组,只要为数组元素分配了内存空间,数组元素就具有了初始值。初始值的获得有两种形式,一种由系统自动分配,另一种由程序员指定。
数组在初始化数组的同时,可以指定数组的大小,也可以分别初始化数组中的每一个元素。在 Java 语言中,初始化数组有以下 3 种方式。
1.3.1 使用 new 指定数组大小后进行初始化
使用 new 关键字创建数组,在创建时指定数组的大小。语法如下:
type[] arrayName = new int[size];
创建数组之后,元素的值并不确定,需要为每一个数组的元素进行赋值,其下标从 0 开始。
例 2
创建包含 5 个元素的 int 类型的数组,然后分别将元素的值设置为 1、2、3、5 和 8。代码如下:
int[] number = new int[5];
number[0] = 1;
number[1] = 2;
number[2] = 3;
number[3] = 5;
number[4] = 8;
如果程序员只指定了数组的长度,那么系统将负责为这些数组元素分配初始值。指定初始值时,系统按如下规则分配初始值。
数组元素的类型是基本类型中的整数类型(byte、short、int 和 long),则数组元素的值是 0。
数组元素的类型是基本类型中的浮点类型(float、double),则数组元素的值是 0.0。
数组元素的类型是基本类型中的字符类型(char),则数组元素的值是‘\u0000’。
数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是 false。
数组元素的类型是引用类型(类、接口和数组),则数组元素的值是 null。
1.3.2 使用 new 指定数组元素的值
使用上述方式初始化数组时,只有在为元素赋值时才确定值。可以不使用上述方式,而是在初始化时就已经确定值。语法如下:
type[] arrayName = new type[]{值 1,值 2,值 3,值 4,• • •,值 n};
例 3
更改例 2 中的代码,使用 new 直接指定数组元素的值。代码如下:
int[] number = new int[]{1, 2, 3, 5, 8};
上述代码的效果等价于例 2 的效果。
注意:不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值,这样会造成代码错误。例如下面代码:
int[] number = new int [5] {1,2,3,4,5};
1.3.3 直接指定数组元素的值
在上述两种方式的语法中,type 可以省略,如果已经声明数组变量,那么直接使用这两种方式进行初始化。如果不想使用上述两种方式,那么可以不使用 new 直接指定数组元素的值。语法如下:
type[] arrayName = {值 1,值 2,值 3,...,值 n};
例 4
在前面例子的基础上更改代码,直接使用上述语法实现 number 数组的初始化。代码如下:
int[] number = {1,2,3,5,8};
使用这种方式时,数组的声明和初始化操作要同步,即不能省略数组变量的类型。如下的代码就是错误的:
int[] number;
number = {1,2,3,5,8};
1.4 获取单个元素
获取单个元素是指获取数组中的一个元素,如第一个元素或最后一个元素。获取单个元素的方法非常简单,指定元素所在数组的下标即可。语法如下:
arrayName[index];
其中,arrayName 表示数组变量,index 表示下标,下标为 0 表示获取第一个元素,下标为 array.length-1 表示获取最后一个元素。当指定的下标值超出数组的总长度时,会拋出 ArraylndexOutOfBoundsException 异常。
例 5
获取 number 数组中的第一个元素、最后一个元素和第六个元素,并将元素的值输出。代码如下:
int[] number = {1,2,3,5,8};
System.out.println("获取第一个元素:"+number[0]);
System.out.println("获取最后一个元素:"+number[number.length-1]);
System.out.println("获取第6个元素:"+number[5]);
/*
执行上述代码,输出结果如下所示:
获取第一个元素:1
获取最后一个元素:8
java.lang.ArrayIndexOutOfBoundsException: 5
*/
我们一共存入了 5 个值,所以下标的取值为 0~4。因为 number[5] 取出的内容超过了这个下标,所以输出结果会提示数组索引超出绑定异常(ArrayIndexOutOfBoundsException)。这一点是在使用数组中是经常出现的问题,大家在编写程序时应该引起注意。
例 6
编写一个 Java 程序,使用数组存放录入的 5 件商品价格,然后使用下标访问第 3 个元素的值。
import java.util.Scanner;
public class Test06 {
public static void main(String[] args) {
int[] prices = new int[5]; // 声明数组并分配空间
Scanner input = new Scanner(System.in); // 接收用户从控制台输入的数据
for (int i = 0; i < prices.length; i++) {
System.out.println("请输入第" + (i + 1) + "件商品的价格:");
prices[i] = input.nextInt(); // 接收用户从控制台输入的数据
}
System.out.println("第 3 件商品的价格为:" + prices[2]);
}
}
/*
运行效果如下所示。
请输入第1件商品的价格:
5
请输入第2件商品的价格:
4
请输入第3件商品的价格:
6
请输入第4件商品的价格:
4
请输入第5件商品的价格:
8
第 3 件商品的价格为:6
*/
上述代码的“int[] prices = new int[5]”语句创建了需要 5 个元素空间的 prices 数组,然后结合 for 循环向数组中的每个元素赋值。
注意:在 Java 中取得数组的长度(也就是数组元素的长度)可以利用“数组名称.length”,返回一个 int 型数据。
数组的索引从 0 开始,而 for 循环中的变量 i 也从 0 开始,因此 score 数组中的元素可以使用 scored 来表示,大大简化了代码。最后使用 prices[2] 获取 prices 数组的第 3 个元素。
1.5 获取全部元素
当数组中的元素数量不多时,要获取数组中的全部元素,可以使用下标逐个获取元素。但是,如果数组中的元素过多,再使用单个下标则显得烦琐,此时使用一种简单的方法可以获取全部元素——使用循环语句。
下面利用 for 循环语句遍历 number 数组中的全部元素,并将元素的值输出。代码如下:
int[] number = {1,2,3,5,8};
for (int i=0;i<number.length;i++) {
System.out.println("第"+(i+1)+"个元素的值是:"+number[i]);
}
除了使用 for 语句,还可以使用 for-each 遍历数组中的元素,并将元素的值输出。代码如下:
for(int val:number) {
System.out.print("元素的值依次是:"+val+"\t");
}
2 二维数组
为了方便组织各种信息,计算机常将信息以表的形式进行组织,然后再以行和列的形式呈现出来。二维数组的结构决定了其能非常方便地表示计算机中的表,以第一个下标表示元素所在的行,第二个下标表示元素所在的列。下面简单了解一下二维数组,包括数组的声明和初始化。
2.1 创建二维数组
在 Java 中二维数组被看作数组的数组,即二维数组为一个特殊的一维数组,其每个元素又是一个一维数组。Java 并不直接支持二维数组,但是允许定义数组元素是一维数组的一维数组,以达到同样的效果。声明二维数组的语法如下:
type arrayName[][]; // 数据类型 数组名[][];
或
type[][] arrayName; // 数据类型[][] 数组名;
其中,type 表示二维数组的类型,arrayName 表示数组名称,第一个中括号表示行,第二个中括号表示列。
下面分别声明 int 类型和 char 类型的数组,代码如下:
int[][] age;
char[][] sex;
2.2 初始化二维数组
二维数组可以初始化,和一维数组一样,可以通过 3 种方式来指定元素的初始值。这 3 种方式的语法如下:
type[][] arrayName = new type[][]{值 1,值 2,值 3,…,值 n}; // 在定义时初始化
type[][] arrayName = new type[size1][size2]; // 给定空间,在赋值
type[][] arrayName = new type[size][]; // 数组第二维长度为空,可变化
例 1
使用第一种方式声明 int 类型的二维数组,然后初始化该二维数组。代码如下:
int[][] temp = new int[][]{{1,2},{3,4}};
上述代码创建了一个二行二列的二维数组 temp,并对数组中的元素进行了初始化。
下图所示为该数组的内存结构。
用第二种方式声明 int 类型的二维数组,然后初始化该二维数组。代码如下:
int[][] temp = new int[2][2];
使用第三种方式声明 int 类型的二维数组,并且初始化数组。代码如下:
int[][] temp = new int[2][];
2.3 获取单个元素
在上部分使用的前 2 种方式创建并初始化了一个二行二列的 int 类型数组 temp。当需要获取二维数组中元素的值时,也可以使用下标来表示。语法如下:
arrayName[i-1][j-1];
其中,arrayName 表示数组名称,i 表示数组的行数,j 表示数组的列数。例如,要获取第二行第二列元素的值,应该使用 temp[1][1]来表示。这是由于数组的下标起始值为 0,因此行和列的下标需要减 1。
例 2
通过下标获取 class_score 数组中第二行第二列元素的值与第四行第一列元素的值。代码如下:
public static void main(String[] args) {
double[][] class_score = {{10.0,99,99},{100,98,97},{100,100,99.5},{99.5,99,98.5}};
System.out.println("第二行第二列元素的值:"+class_score[1][1]);
System.out.println("第四行第一列元素的值:"+class_score[3][0]);
}
/*
执行上述代码,输出结果如下:
第二行第二列元素的值:98.0
第四行第一列元素的值:99.5
*/
2.4 获取全部元素
在一维数组中直接使用数组的 length 属性获取数组元素的个数。而在二维数组中,直接使用 length 属性获取的是数组的行数,在指定的索引后加上 length(如 array[0].length)表示的是该行拥有多少个元素,即列数。
如果要获取二维数组中的全部元素,最简单、最常用的办法就是使用 for 语句。在一维数组全部输出时,我们使用一层 for 循环,而二维数组要想全部输出,则使用嵌套 for 循环(2 层 for 循环)。
例 3
使用 for 循环语句遍历 double 类型的 class_score 数组的元素,并输出每一行每一列元素的值。代码如下:
public static void main(String[] args) {
double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
for (int i = 0; i < class_score.length; i++) { // 遍历行
for (int j = 0; j < class_score[i].length; j++) {
System.out.println("class_score[" + i + "][" + j + "]=" + class_score[i][j]);
}
}
}
/*
执行上述代码,输出结果如下所示。
class_score[0][0]=100.0
class_score[0][1]=99.0
class_score[0][2]=99.0
class_score[1][0]=100.0
class_score[1][1]=98.0
class_score[1][2]=97.0
class_score[2][0]=100.0
class_score[2][1]=100.0
class_score[2][2]=99.5
class_score[3][0]=99.5
class_score[3][1]=99.0
class_score[3][2]=98.5
*/
上述代码使用嵌套 for 循环语句输出二维数组。在输出二维数组时,第一个 for 循环语句表示以行进行循环,第二个 for 循环语句表示以列进行循环,这样就实现了获取二维数组中每个元素的值的功能。
例 4
假设有一个矩阵为 5 行 5 列,该矩阵是由程序随机产生的 10 以内数字排列而成。下面使用二维数组来创建该矩阵,代码如下:
public class Test11 {
public static void main(String[] args) {
// 创建一个二维矩阵
int[][] matrix = new int[5][5];
// 随机分配值
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] = (int) (Math.random() * 10);
}
}
System.out.println("下面是程序生成的矩阵\n");
// 遍历二维矩阵并输出
for (int k = 0; k < matrix.length; k++) {
for (int g = 0; g < matrix[k].length; g++) {
System.out.print(matrix[k][g] + "");
}
System.out.println();
}
}
}
/*
运行该程序的结果如下所示。
下面是程序生成的矩阵
78148
69230
43823
75663
05688
*/
在该程序中,首先定义了一个二维数组,然后使用两个嵌套的 for 循环向二维数组中的每个元素赋值。其中,Math.random() 方法返回的是一个 double 类型的数值,数值为 0.6、0.9 等,因此乘以 10 之后为 10 以内的整数。最后又使用了两个嵌套的 for 循环遍历二维数组,输出二维数组中的值,从而产生矩阵。
for each 循环语句不能自动处理二维数组的每一个元素。它是按照行, 也就是一维数组处理的。要想访问二维教组 a 的所有元素, 需要使用两个嵌套的循环, 如下所示:
for (double[] row : a) {
for (double value : row) {
......
}
}
把【例2】修改为使用 for each 循环语句输出,代码如下所示:
public static void main(String[] args) {
double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
for (double[] row : class_score) {
for (double value : row) {
System.out.println(value);
}
}
}
/*
输出结果为:
100.0
99.0
99.0
100.0
98.0
97.0
100.0
100.0
99.5
99.5
99.0
98.5
*/
提示:要想快速地打印一个二维数组的数据元素列表,可以调用:
System.out.println(Arrays.deepToString(arrayName));
代码如下:
System.out.println(Arrays.deepToString(class_score));
/*输出格式为:
[[100.0, 99.0, 99.0], [100.0, 98.0, 97.0], [100.0, 100.0, 99.5], [99.5, 99.0, 98.5]]
*/
2.5 获取整行元素
除了获取单个元素和全部元素之外,还可以单独获取二维数组的某一行中所有元素的值,或者二维数组中某一列元素的值。获取指定行的元素时,需要将行数固定,然后只遍历该行中的全部列即可。
例 5
编写一个案例,接收用户在控制台输入的行数,然后获取该行中所有元素的值。代码如下:
public static void main(String[] args) {
double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
Scanner scan = new Scanner(System.in);
System.out.println("当前数组只有" + class_score.length + "行,您想查看第几行的元素?请输入:");
int number = scan.nextInt();
for (int j = 0; j < class_score[number - 1].length; j++) {
System.out.println("第" + number + "行的第[" + j + "]个元素的值是:" + class_score[number - 1][j]);
}
}
/*
执行上述代码进行测试,输出结果如下所示。
当前数组只有4行,您想查看第几行的元素?请输入:
3
第3行的第[0]个元素的值是:100.0
第3行的第[1]个元素的值是:100.0
第3行的第[2]个元素的值是:99.5
*/
2.6 获取整列元素
获取指定列的元素与获取指定行的元素相似,保持列不变,遍历每一行的该列即可。
例 6
编写一个案例,接收用户在控制台中输入的列数,然后获取二维数组中所有行中该列的值。代码如下:
public static void main(String[] args) {
double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
Scanner scan = new Scanner(System.in);
System.out.println("您要获取哪一列的值?请输入:");
int number = scan.nextInt();
for (int i = 0; i < class_score.length; i++) {
System.out.println("第 " + (i + 1) + " 行的第[" + number + "]个元素的值是" + class_score[i][number]);
}
}
/*
执行上述代码进行测试,如下所示。
您要获取哪一列的值?请输入:
2
第 1 行的第[2]个元素的值是99.0
第 2 行的第[2]个元素的值是97.0
第 3 行的第[2]个元素的值是99.5
第 4 行的第[2]个元素的值是98.5
*/
3 多维数组
除了一维数组和二维数组外,Java 中还支持更多维的数组,如三维数组、四维数组和五维数组等,它们都属于多维数组。经过前面一维,二维的练习后不难发现,想要提高数组的维数,只要在声明数组时将索引与中括号再加一组即可,所以三维数组的声明为 int score[ ][ ][ ]
,而四维数组为 int score[ ][ ][ ][ ]
,以此类推。
通常也将二维数组看作是多维数组。本文以三维数组为例来介绍多维数组。
三维数组有三个层次,可以将三维数组理解为一个一维数组,其内容的每个元素都是二维数组。依此类推,可以获取任意维数的数组。
多维数组的声明、初始化和使用都与二维数组相似,因此这里不再进行具体说明。
例 1
假设程序中有一个名为 namelist 的 String 类型三维数组,下面编写代码对它进行遍历,输出每个元素的值。代码如下:
public static void main(String[] args) {
String[][][] namelist = { { { "张阳", "李风", "陈飞" }, { "乐乐", "飞飞", "小曼" } },
{ { "Jack", "Kimi" }, { "Lucy", "Lily", "Rose" } }, { { "徐璐璐", "陈海" }, { "李丽丽", "陈海清" } } };
for (int i = 0; i < namelist.length; i++) {
for (int j = 0; j < namelist[i].length; j++) {
for (int k = 0; k < namelist[i][j].length; k++) {
System.out.println("namelist[" + i + "][" + j + "][" + k + "]=" + namelist[i][j][k]);
}
}
}
}
/*
执行上述代码,输出结果如下所示。
namelist[0][0][0]=张阳
namelist[0][0][1]=李风
namelist[0][0][2]=陈飞
namelist[0][1][0]=乐乐
namelist[0][1][1]=飞飞
namelist[0][1][2]=小曼
namelist[1][0][0]=Jack
namelist[1][0][1]=Kimi
namelist[1][1][0]=Lucy
namelist[1][1][1]=Lily
namelist[1][1][2]=Rose
namelist[2][0][0]=徐璐璐
namelist[2][0][1]=陈海
namelist[2][1][0]=李丽丽
namelist[2][1][1]=陈海清
*/
码字不易 求个三连