1 数组概述
1.1 为什么需要数组
问题:Java 考试结束后,老师给张浩分配了一项任务, 让他计算全班学员 ( 30 人)的平均分。
张浩想了一下,要计算平均分不难,首先要定义变量。可是班里有 30 个学员,就要定义 30 个变量。这么一想,就开始头疼了。不过没办法,师命难违。因此,他写出了下面的代码。
int score1 = 95;
int score2 = 89;
int score3 = 79;
int score4 = 64;
int score5 = 76;
int score6 = 88;
int score28 = 70;
int score29 = 88;
int score30 = 65;
average = (score1+score2+score3+score4+score5+……+score30) /30
上面的代码缺陷很明显,首先是定义的变量的个数太多,如果存储 10000 个学员的成绩,难道真要定义 10000 个变量吗?这显然不可能,另外也不利于数据处理。例如,求所有成绩之和或最高分、输出所有成绩,就需要把所有的变量名都写出来,这显然不是一种好的方法。
Java 针对此类问题提供了有效的存储方式——数组。
1.2 Java 中的数组
在 Java 中,数组就是一个变量,用于将相同数据类型的数据存储在内存中。数组中的每一个数据元素都属于同一数据类型。例如,全班 30 个学员的成绩都是整型,就可以存储在一个整型数组里面。
在第 2 章中已经学过,声明一个变量就是在内存空间分配一块合适的空间,然后将数据存储在这个空间中。同样,数组就是在内存空间划出一串连续的空间,如图 8.1 所示。
图 8.1 内存中的 int 类型变量和 int 类型数组
了解了数组在内存中的存储方式,下面来看数组的基本要素。
标识符。首先,和变量一样,在计算机中,数组也要有一个名称,称为标识符,用于区分不同的数组。
数组元素。当给出了数组名称,即数组标识符后,要向数组中存放数据,这些数据就称为数组元素。
数组下标。在数组中,为了正确地得到数组的元素,需要对它们进行编号,这样计算机才能根据编号去存取,这个编号就称为数组下标。
元素类型。存储在数组中的数组元素应该是同一数据类型,如可以把学员的成绩存储在数组中,而每一个学员的成绩可以用整型变量存储,因此称它的元素类型是整型。
根据上面的分析,可以得到如图 8.2 所示的数组结构。
图 8.2 数组的基本结构
对于图 8.2, 做如下说明。
数组只有一个名称,即标识符,如 scores 。
数组元素在数组里顺序排列编号,该编号即为数组下标,它标明了元素在数组中的位置。第一个元素的编号规定为 0, 因此数组的下标依次为0、1、2、3、4等。
数组中的每个元素都可以通过下标来访问。由于元素是按顺序存储的,每个元素固定对应—个下标,因此可以通过下标快速地访问到每个元素。例如,scores[0] 指数组中的第一个元素 70, scores[1] 指数组中的第二个元素 100 。
数组的大小(长度)是数组可容纳元素的最大数量。定义一个数组的同时也定义了它的大小。如果数组已满但是还继续向数组中存储数据的话,程序就会出错,这称为数组越界。
例如,图 8.2 中数组下标最大为 3, 如果数组的下标超过此大小,程序就会因错误而终止。
2 如何使用数组
2.1 使用数组的步骤
前面已经学习了数组的基本结构,那么数组该如何使用呢?其实只需要 4 个步骤。
1.声明数组
在 Java 中,声明数组的语法如下。
语法:
数据类型[]数组名;
或者
数据类型 数组名[];
以上两种方式都可以声明一个数组,数组名可以是任意合法的变量名。
声明数组就是告诉计算机该数组中数据的类型是什么。例如:
int [] scores; //存储学员的成绩,类型为 int
double height [] ; //存储学员的身高,类型为 double.
String [] names; //存储学员的姓名,类型为 String
2.分配空间
虽然声明了数组,但并不会为数组元素分配内存空间,此时还不能使用数组。因此要为数组分配内存空间,这样数组的每一个元素才能对应一个存储单元。
简单地说,分配空间就是要告诉计算机在内存中分配一些连续的空间来存储数据。在 Java 中可以使用 new 关键字来给数组分配空间。
语法:
数组名 = new 数据类型[数组长度];
其中,数组长度就是数组中能存放的元素个数,显然应该为大于 0 的整数。例如:
scores = new int [30] ; //长度为 30 的 int 类型数组
height = new double [30] ; //长度为 30 的 double 类型数组
names = new String [30] ; //长度为 30 的 String 类型数组
可以将上面两个步骤合并,即在声明数组的同时就给它分配空间,语法如下。
语法:
数据类型[] 数组名= new 数据类型[数组长度];
例如:
int scores [] = new int [30]; //存储 30 个学员成绩
一旦声明了数组的大小就不能再修改,即数组的长度是固定的。例如,上面名称为 scores 的数组长度是 30, 假如发现有 31 个学员成绩需要存储,想把数组长度改为 31, 则不可以,只能重新声明新的数组。
3.赋值
分配空间后就可以向数组里放数据了。数组中的每一个元素都是通过下标来访问的,语法如下。
数组名[下标值];
例如,向 scores 数组中存放数据。
scores [0] = 89;
scores [1] = 60;
scores [2] = 70;
……
回想 8.1 节提出的问题,张浩要计算 30 位学员的平均分,也是这样一个一个地赋值,非常烦琐。仔细观察上面的代码,会发现数组的下标是规律变化的,即从 0 开始顺序递增,所以考虑用循环变量表示数组下标,从而利用循环给数组赋值。
Scanner input = new Scanner(System.in);
for (int i = 0; i < 30; i++) {
score [i] = input .nextInt () ; //从控制台接受键盘输入进行循环赋值
}
可见,运用循环大大简化了代码。
注意:在编写程序时,数组和循环往往结合在一起使用,可以大大简化代码,提高程序效率。通常,使用 for 循环遍历数组或者给数组元素赋值。
在 Java 中还提供了另外一种创建数组的方式,它将声明数组、分配空间和赋值合并完成,语法如下。
数据类型[]数组名={值 1, 值 2, 值 3,…… ,值 n};
例如,使用这种方式来创建 scores 数组。
int [] scores = {60,70,98,90,76}; //创建一个长度为 5 的数组 scores
同时,它也等价于下面的代码:
int [ ] scores = new int[]{60,70,98,90,76}; //[]里面必须为空
经验:值得注意的是,直接创建并赋值的方式一般在数组元素比较少的情况下使用。它必须一并完成,如下代码是不合法的。
int[] score;
score = {60,70,98, 90,76}; //错误
4. 对数据进行处理
现在使用数组解决 8.1 节提出的计算 30 位学员平均分的问题。为了简单起见,先计算五位学员的平均分,如示例 1 所示。
示例1
package cn.jbit.array;
import java.util.Scanner;
public class ArrayDemo {
public static void main(String[] args) {
int[] scores = new int[5]; //成绩数组
int sum = 0; //成绩总和
Scanner input = new Scanner(System.in);
System.out.println("请输入5位学员的成绩:");
for (int i = 0; i < scores.length; i++) {
scores[i] = input.nextInt();
sum = sum + scores[i]; //成绩累加
}
//计算并输出平均分
System.out.println("学员的平均分是:" + (double)sum/scores.length);
}
}
示例 1 的运行结果如图 8.3 所示。
图 8.3 示例 1 的运行结果
在循环中,循环变量 i 从 0 开始递增直到数组的最大长度 scores.length。因此,每次循环 i 加 1 ,实现数组的每个元素和的累加。
注意:数组一经创建,其长度(数组中包含元素的数目)是不可改变的,如果越界访问(即数组下标超过 0 至数组长度 -1 的范围),程序会报错。因此,当我们需要使用数组长度时,一般用如下的方式。
数组名.length;
例如,示例 1 的代码中,循环变量 i 小于数组长度,我们写成
i < scores.length;
而不是写成
i < 5;
如果定义的数组是基本数据类型的数组,即 int、double、char 和 boolean 类型,在 Java中定义数组之后,若没有指定初始值,则依数据类型的不同,会给数组元素赋一个默认值,如表 8-1 所示。
2.2 常见错误
数组是编程中常用的存储数据的结构,但在使用的过程中会出现一些错误,在这里进行归纳,希望能够引起大家的重视。
1.数组下标从0开始
常见错误1
package cn.jbit.array;
public class ErrorDemo1 {
public static void main(String[] args) {
int[] scores = new int[]{90, 85, 65, 89, 87};
System.out.println("第3位同学的成绩应修改为92");
scores[3] = 92;
System.out.println("修改后,5位同学的成绩是:");
for (int i = 0; i < scores.length; i++) { //通过 for 循环输出数组元素,即遍历数组
System.out.print(scores[i] + " ");
}
}
}
程序运行结果如图 8.4 所示。
图 8.4 常见错误 1 的运行结果
由运行结果可以看到,第三位同学的成绩仍然是 65, 而第四位同学的成绩变成了92。分析原因,是第三位同学的成绩在数组中的下标是 2, 而不是 3 。
排错方法:将赋值语句改为 scores[2] = 92 。
这样再运行程序,就可以将第三位同学的成绩修改为 92 分。
2.数组访问越界
常见错误2
package cn.jbit.array;
public class ErrorDemo2 {
public static void main(String[] args){
int[] scores = new int[2];
scores[0] = 90;
scores[1] = 85;
scores[2] = 65;
System.out.println(scores[2]);
}
}
运行程序,发现编译器报错,如图 8.5 所示。
图 8.5 常见错误 2 的运行结果
如图 8.5 中所示,控制台打印出了 "java.lang.ArraylndexOutOfBoundsException", 意思是数组下标超过范围,即数组越界,这是异常类型(关于异常将在后续课程中学习,这里可以简单理解为程序能捕获的错误)。 "ErrorDemo2.java:9" 指出了出错位置,这里是程序的第 9 行,即 scores[2] = 65;。
因为数组下标范围是 0〜数组长度-1, 所以上面的数组下标范围是 0〜1, 而程序中的下标出现了 2, 超出了该范围,造成数组访问越界,所以编译器报错。
排错方法:增加数组长度或删除超出数组下标范围的语句。
注意:数组下标从 0 开始,而不是从 1 开始。如果访问数组元素时指定的下标小于 0 ,或者大于等于数组的长度,都将出现数组下标越界异常