数组是指一组类型相同的数据的集合,数组中的每个数据被称作元素。数组可以存放任意类型的元素,但同一个数组里存放的元素类型必须一致。
数组的定义
在Java中,声明数组的方式有两种。
第一种方式: 数据类型[] 数组名 = null;
第二种方式: 数据类型[] 数组名;
数组名= new数据类型[长度];
int[] x; // 声明一个int[]类型的变量
x = new int[100]; // 为数组x分配100个元素空间
上述语句就相当于在内存中定义了100个int类型的变量,第一个变量的名称为x[0],第二个变量的名称为x[1],以此类推,第100个变量的名称为x[99],这些变量的初始值都是0。
第一行代码声明了一个变量x,该变量的类型为int[],即声明了一个int类型的数组。变量x会占用一块内存单元,它没有被分配初始值。变量x的内存状态如下图。
第二行代码 x = new int[100]; 创建了一个数组,将数组的地址赋值给变量x。在程序运行期间可以使用变量x引用数组,这时变量x在内存中的状态会发生变化,如下图。
上图描述了变量x引用数组的情况。该数组中有100个元素,初始值都为0。数组中的每个元素都有一个索引(也可称为角标),要想访问数组中的元素可以通过“x[0]、x[1]、……、x[98]、x[99]”的形式。需要注意的是,数组中最小的索引是0,最大的索引是“数组的长度-1”。在Java中,为了方便获得数组的长度,提供了一个length属性,在程序中可以通过“数组名.length”的方式获得数组的长度,即元素的个数。
接下来通过一个案例来演示如何定义数组以及访问数组中的元素。
public class Example22 {
public static void main(String[] args) {
int[] arr; // 声明变量
arr = new int[3]; // 创建数组对象
System.out.println("arr[0]=" + arr[0]); // 访问数组中的第一个元素
System.out.println("arr[1]=" + arr[1]); // 访问数组中的第二个元素
System.out.println("arr[2]=" + arr[2]); // 访问数组中的第三个元素
System.out.println("数组的长度是:" + arr.length); // 打印数组长度
}
}
上述代码中,第3行代码声明了一个int[]类型变量arr,第4行代码创建了一个长度为3的数组,并将数组在内存中的地址赋值给变量arr。在第5~7行代码中,通过索引访问数组中的元素,第8行代码通过length属性访问数组中元素的个数。从打印结果可以看出,数组的长度为3,且三个元素初始值都为0,这是因为当数组被成功创建后,如果没有给数组元素赋值,则数组中元素会被自动赋予一个默认值,根据元素类型的不同,默认初始化的值也是不一样的。
数组不进行初始化,系统默认初始化,不同类型数组元素的默认值如下表。
数据类型 | 默认初始化值 |
byte、short、int、long | 0 |
float、double | 0.0 |
char | 一个空字符,即’\u0000’ |
boolean | false |
引用数据类型 | null,表示变量不引用任何对象 |
接下来通过一个案例来学习如何为数组的元素赋值。
public class Example23 {
public static void main(String[] args) {
int[] arr = new int[4]; // 定义可以存储4个元素的整数类型数组
arr[0] = 1; // 为第1个元素赋值1
arr[1] = 2; // 为第2个元素赋值2
//依次打印数组中每个元素的值
System.out.println("arr[0]=" + arr[0]);
System.out.println("arr[1]=" + arr[1]);
System.out.println("arr[2]=" + arr[2]);
System.out.println("arr[3]=" + arr[3]);
}
}
上述代码中,第3行代码定义了一个数组,此时数组中每个元素都为默认初始值0。第4~5行代码通过赋值语句将数组中的元素arr[0]和arr[1]分别赋值为1和2,而元素arr[2]和arr[3]没有赋值,其值仍为0,因此打印结果中四个元素的值依次为1、2、0、0。
在定义数组时只指定数组的长度,由系统自动为元素赋初值的方式称作动态初始化。在初始化数组时还有一种方式叫做静态初始化,就是在定义数组的同时就为数组的每个元素赋值。数组的静态初始化有两种方式,具体格式如下:
类型[] 数组名 = new 类型[]{元素,元素,……};
类型[] 数组名 = {元素,元素,元素,……};
接下来通过一个案例演示数组静态初始化的效果。
public class Example24 {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 }; // 静态初始化
//依次访问数组中的元素
System.out.println("arr[0] = " + arr[0]);
System.out.println("arr[1] = " + arr[1]);
System.out.println("arr[2] = " + arr[2]);
System.out.println("arr[3] = " + arr[3]);
}
}
上述代码中,采用静态初始化的方式为每个元素赋予初值,其值分别是1、2、3、4。需要注意的是,文件中的第3行代码千万不可写成int[] x = new int[4]{1,2,3,4};,这样写编译器会报错。原因在于编译器会认为数组限定的元素个数[4]与实际存储的元素{1,2,3,4}个数有可能不一致,存在一定的安全隐患。
脚下留心:数组索引
数组是一个容器,存储到数组中的每个元素,都有自己的自动编号,最小值为0,最大值为数组长度-1,如果要访问数组存储的元素,必须依赖于索引。在访问数组的元素时,索引不能超出0~length-1范围,否则程序会报错。
接下来通过一个案例演示索引超出数组范围的情况。
public class Example25 {
public static void main(String[] args) {
int[] arr = new int[4]; // 定义一个长度为4的数组
System.out.println("arr[0]=" + arr[4]); // 通过索引4访问数组元素
}
}
运行结果中所提示的错误信息是数组越界异常ArrayIndexOutOfBoundsException,出现这个异常的原因是数组的长度为4,索引范围为0~3,Example25类中的第4行代码使用索引4访问元素时超出了数组的索引范围。所谓异常指程序中出现的错误,它会报告出错的异常类型、出错的行号以及出错的原因。
在使用变量引用一个数组时,变量必须指向一个有效的数组对象,如果该变量的值为null,则意味着没有指向任何数组,此时通过该变量访问数组的元素会出现空指针异常,接下来通过一个案例来演示这种异常。
public class Example26 {
public static void main(String[] args) {
int[] arr = new int[3]; // 定义一个长度为3的数组
arr[0] = 5; // 为数组的第一个元素赋值
System.out.println("arr[0]=" + arr[0]); // 访问数组的元素
arr = null; // 将变量arr置为null
System.out.println("arr[0]=" + arr[0]); // 访问数组的元素
}
}
从运行结果可以看出,Example26类的第4~5行代码都能通过变量arr正常地操作数组。第6行代码将变量置为null,第7行代码再次访问数组时就出现了空指针异常NullPointerException。
数组的常见操作
1.数组遍历
在操作数组时,经常需要依次访问数组中的每个元素,这种操作称作数组的遍历。接下来通过一个案例学习如何使用for循环遍历数组。
public class Example27 {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 }; // 定义数组
// 使用for循环遍历数组的元素
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]); // 通过索引访问元素
}
}
}
2.数组最值
在操作数组时,经常需要获取数组中元素的最值。接下来通过一个案例来演示如何获取数组中元素的最大值。
public class Example28 {
public static void main(String[] args) {
int[] arr = { 4, 1, 6, 3, 9, 8 }; // 定义一个数组
int max = getMax(arr); // 调用获取元素最大值的方法
System.out.println("max=" + max); // 打印最大值
}
static int getMax(int[] arr) {
int max = arr[0]; // 定义变量max用于记住最大数,首先假设第一个元素为最大值
// 下面通过一个for循环遍历数组中的元素
for (int x = 1; x < arr.length; x++) {
if (arr[x] > max) { // 比较 arr[x]的值是否大于max
max = arr[x]; // 条件成立,将arr[x]的值赋给max
}
}
return max; // 返回最大值max
}
}
上述代码中,第7~16行代码定义的getMax()方法用于求数组中的最大值,该方法定义了一个临时变量max,用于记录数组的最大值。首先假设数组中第一个元素arr[0]为最大值,然后使用for循环对数组进行遍历,在遍历的过程中只要遇到比max值还大的元素,就将该元素赋值给max。这样一来,变量max就能够在循环结束时记录数组中的最大值。需要注意的是,for循环中的变量i是从1开始的,原因是程序已经假设第一个元素为最大值,for循环只需要从第二个元素开始比较。第4行代码调用getMax()函数获取数据arr的最大值,由上图可知,数组arr中的最大值为9。
3.数组排序
在操作数组时,经常需要对数组中的元素进行排序。下面为读者介绍一种比较常见的排序算法——冒泡排序。在冒泡排序的过程中,不断地比较数组中相邻的两个元素,较小者向上浮,较大者往下沉,整个过程和水中气泡上升的原理相似。
第一步,从第一个元素开始,将相邻的两个元素依次进行比较,如果前一个元素比后一个元素大,则交换它们的位置,直到最后两个元素完成比较。整个过程完成后,数组中最后一个元素自然就是最大值,这样也就完成了第一轮比较。
第二步,除了最后一个元素,将剩余的元素继续进行两两比较,过程与第一步相似,这样就可以将数组中第二大的元素放在倒数第二个位置。
第三步,以此类推,持续对越来越少的元素重复上面的步骤,直到没有任何一对元素需要比较为止。
接下来通过一个案例实现冒泡排序。
public class Example29 {
public static void main(String[] args) {
int[] arr = { 9, 8, 3, 5, 2 };
System.out.print("冒泡排序前 :");
printArray(arr); // 打印数组元素
bubbleSort(arr); // 调用排序方法
System.out.print("冒泡排序后 :");
printArray(arr); // 打印数组元素
}
// 定义打印数组元素的方法
public static void printArray(int[] arr) {
// 循环遍历数组的元素
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " "); // 打印元素和空格
}
System.out.print("\n");
}
// 定义对数组排序的方法
public static void bubbleSort(int[] arr) {
// 定义外层循环
for (int i = 0; i < arr.length - 1; i++) {
// 定义内层循环
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) { // 比较相邻元素
// 下面的三行代码用于交换两个元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
System.out.print("第" + (i + 1) + "轮排序后:");
printArray(arr); // 每轮比较结束打印数组元素
}
}
}
在上述代码中,定义了bubbleSort ()方法,在bubbleSort()方法中通过嵌套for循环实现数组元素的冒泡排序,外层循环用来控制进行多少轮比较,每一轮比较都可以确定一个元素的位置,由于最后一个元素不需要进行比较,因此外层循环的次数为arr.length-1。内层循环的循环变量用于控制每轮比较的次数,它被作为索引用于访问数组的元素。由于变量在循环过程中是自增的,因此可以实现相邻元素依次进行比较。
在每次比较时如果前者小于后者,就交换两个元素的位置,元素交换过程如下图。
在上图中,第一轮比较中,第一个元素“9”为最大值,因此它在每次比较时都会发生位置的交换,被放到最后一个位置。第二轮比较与第一轮过程类似,元素“8”被放到倒数第二个位置。第三轮比较中,第一次比较没有发生位置的交换,在第二次比较时才发生位置交换,元素“5”被放到倒数第三个位置。第四轮比较只针对最后两个元素,它们比较后发生了位置的交换,元素“3”被放到第二个位置。通过四轮比较,数组中的元素已经完成了排序。
在交换两个元素时,首先定义了一个变量temp用于记录数组元素arr[j]的值,然后将arr[j+1]的值赋给arr[j],最后再将temp的值赋给arr[j+1],这样便完成了两个元素的交换。交换过程如下图。
二维数组
二维数组的定义有很多方式。
第一种方式: 数据类型[][] 数组名 = new 数据类型[行的个数][列的个数];
下面以第一种方式声明一个数组,如下所示。
int[][] xx= new int[3][4];
上面的代码相当于定义了一个3*4的二维数组,即3行4列的二维数组,接下来通过一个图表示xx[3][4]。
第二种方式: 数据类型[][] 数组名 = new int[行的个数][];
下面以第二种方式声明一个数组,如下所示。
int[][] xx= new int[3][];
第二种方式和第一种类似,只是数组中每个元素的长度不确定。
第三种方式: 数据类型[][] 数组名= {{第0行初始值},{第1行初始值},...,{第n行初始值}};
下面以第三种方式声明一个数组,如下所示。
int[][] xx= {{1,2},{3,4,5,6},{7,8,9}};
上面的二维数组arr中定义了三个元素,这三个元素都是数组,分别为{1,2}、{3,4,5,6}、{7,8,9}。
二维数组中元素的访问也是通过索引的方式。例如,访问二维数组arr中第一个元素数组的第二个元素,具体代码如下:
arr[0][1];
接下来通过一个案例演示二维数组的使用,该案例要统计一个公司三个销售小组中每个小组的总销售额以及整个公司的销售额。
public class Example30 {
public static void main(String[] args) {
int[][] arr = new int[3][]; // 定义一个长度为3的二维数组
arr[0] = new int[] { 11, 12 }; // 为数组的元素赋值
arr[1] = new int[] { 21, 22, 23 };
arr[2] = new int[] { 31, 32, 33, 34 };
int sum = 0; // 定义变量记录总销售额
for (int i = 0; i < arr.length; i++) { // 遍历数组元素
int groupSum = 0; // 定义变量记录小组销售总额
for (int j = 0; j < arr[i].length; j++) { // 遍历小组内每个人的销售额
groupSum = groupSum + arr[i][j];
}
sum = sum + groupSum; // 累加小组销售额
System.out.println("第" + (i + 1) + "小组销售额为:" + groupSum + " 万元。");
}
System.out.println("总销售额为: " + sum + " 万元。");
}
}
上述代码中,第3行代码定义了一个长度为3的二维数组arr;第4~6行代码为数组arr的每个元素赋值。文件中还定义了两个变量sum和groupSum,其中sum用于记录公司的总销售额,groupSum用于记录每个销售小组的销售额。第8~15行代码通过嵌套for循环统计销售额,外层循环对三个销售小组进行遍历,内层循环对每个小组员工的销售额进行遍历,内层循环每循环一次就相当于将一个小组员工的销售额累加到本小组的销售总额groupSum中。内层循环结束,相当于本小组销售总金额计算完毕,把groupSum的值累加到sum中。当外层循环结束时,三个销售小组的销售总额groupSum都累加到了sum中,统计出整个公司的销售总额。
【案例2-6】 登录注册
【案例2-7】 抽取幸运观众