捌——常用的数据结构
一、理解数组
数组(Array),顾名思义就是一组数据。这组数据要有一定的关系,不然只会使问题更复杂。
在 Java 中,数组也可以视为一种数据类型。它本身是一种引用类型。
引用类型(reference type)引用类型的变量非常类似于C/C++的指针。而指针就是变量在内存中的地址。任何变量只要存在于内存中,就需要有个唯一的编号标识这个变量在内存中的位置,而这个唯一的内存编号就是内存地址,它就是所谓的指针(pointer)。这些指针是为了我们访问(或定位)某个事物(或变量)而发明的一种机制。找到了内存地址,也就可以找到地址所对应的变量。
对于一个 32 位或 64 位的内存地址,为了方便操作,给这个 32 位或 64 位的地址取一个好记的名称,在C/C++中这个名称就叫做指针变量。而放到 Java 中,就叫引用类型变量。这两种语言在处理的细节上还是有所区别的。因为C++中,引用的概念就是某个变量的别名(alias)。但 Java 中的 “ 引用 ” 和 C++ 中的 “ 引用 ” 是有不同的,Java 中的 “ 引用 ” 数据类型更像C/C++中的地址指针,其不同的是,Java 为了方便用户,做了二次包装,只不过这个内部转换为用户是 “ 透明 ” 的。
Java 的数组既可以存储基本类型的数据,也可以存储引用数据类型的数据。例如 int 是一个基本类型,但 int[](把 “ int[] ” 当成一个整体)就是一种引用数据类型。
下面简要描述下这两种变量的定义方式,二者在地位上是对等的。
int x; //基本数据类型
int[] x; //引用数据类型
int[] 就是一种数据类型,与 int、float等基本数据类型类似,同样可以使用该类型来定义变量,也可以使用该类型进行类型转换等。在使用 int[] 类型来定义变量、进行类型转换时,与使用其他基本数据类型的使用方法没有任何区别。
例如,我们存储 12 个月份的天数,可以写成如下形式:
int[] month = {31,28,31,30,31,30,31,31,30,31,30,31};
这种称为静态初始化,在声明的同时给出初始数据。
若想在 Java 中取得数组的长度(也就是数组元素的个数)可以利用 “ .length ” 完成。在 Java 中,一切皆为对象,事实上,length 是数组对象的一个属性,可以通过点操作符 “ . ” 来访问。
因此,若要取得所定义的数组 month 的元素个数,只要在数组 month的名称后面加上 “ .lenght ”即可,可以写成如下形式:
month.length; //取得数组的长度
另外,数组是从 0 开始索引的。也就是说,数组 month 的第一个元素是:
month[0]; //取得下标为0的数,也就是第1个数
二、一维数组
我们可以给数组一个 “ 定义 ”:数组是有序数据的集合,数组中的每个元素必须是相同的数据类型,可以用一个统一的数组名和下标来唯一地确定数组中的元素。
一维数组可以存放千万个数据,并且这些数据的类型是完全相同的。
1.一维数组的声明与内存的分配
要使用 Java 的数组,必须经过以下两个步骤。
(1)声明数组。
(2)分配内存给该数组。
语法如下:
数据类型[] 数组名; //声明一维数组
数组名 = new 数据类型[个数]; //分配内存给数组
在数组的声明格式里,“ 数据类型 ” 是声明数组元素的数据类型,常见的类型有整型、浮点型与字符型等。“ 数组名 ” 是用来统一这组相同数据类型的元素的名称,其命名规则和变量相同。数组声明后,接下来配置数组所需的内存,其中 “ 个数 ” 是告诉编译器,所声明的数组要存放多少个元素,而关键字 “ new ” 则是命令编译器根据括号里的个数,在内存中分配一块内存供该数组使用。
例如:
int[] score; //声明整型数组score
score = new int[3]; //为整型数组score分配内存空间,其元素个数为3
当声明一个整型数组 score 时,可将 score 视为数组类型的变量,此时这个变量并没有包含任何内容,编译器仅会分配一块内存给它,用来保存指向数组实体的地址。
声明之后,接着要进行内存分配的操作,也就是第二行语句。这一行会开辟 3 个可供保存整数的内存空间,并把次内存空间的参考地址赋给 score 变量。
此处的参考地址的值会因环境的不同而异。数组属于费基本数据类型,因此数组变量 score 所保存的并非是数组的实体,而是数组实体的参考地址。
除了用两行来声明并分配内存给数组之外,也可以用较为简洁的方式,把两行缩成一行来编写,其格式如下:
数据类型[] 数组名 = new 数据类型[个数];
上方的格式会在声明的同时,即分配一块内存空间供该数组使用。
注意:在 Java 中,整数数据类型所占用的空间为 4 个 bytes。
2.数组中元素的表示方法
想要使用数组里的元素,可以利用索引来完成。Java 的数组索引编号从 0 开始,以一个名为 score 长度为 11 的整型数组为例,score[0] 代表第 1 个元素,score[1] 代表第 2 个元素,以此类推,score[10]为数组中的第 11个元素(也就是最后一个元素)。
注意:整型数组中的数据默认为 0。
3.数组元素的赋值
其格式为:
数据类型[] 数组名 = {初值0,初值1,…,初值n}
下为一个更灵活的数组赋值方法:
//数组元素更灵活的赋值方法
import java.util.Random; //引用java.util.Random包
public class show_Array{
public static void main(String[] args){
Random rand = new Random(); //创建一个Random对象
int[] a = null; //声明整型数组a
int[] b = null; //声明整型数组b
//动态申请内存,rand.nextInt(10)返回一个[0,10)的随机整型数
a = new int[rand.nextInt(10)];
b = a;
System.out.println("数组a的长度为:" + a.length);
System.out.println("数组b的长度为:" + b.length + "\n");
for(int i = 0;i < a.length;i++){
//rand.nextInt(100)返回一个[0,100)的随机整型数
a[i] = rand.nextInt(100);
System.out.print("a[" + i + "] = " + a[i] + "\t");
System.out.println("b[" + i + "] = " + b[i]);
}
}
}
事实上,这里的 b = a; 的含义是将 a 数组的引用赋值给数组 b。这时 a 和 b 指向的数组对象是相同的。换句话说,此时的 a 和 b 是 “ 一套数据,两套名字 ”,这就是 Java 中广泛使用的概念——引用(reference)。
4.数组应用范例
求数组中的最大值和最小值
//求得数组中的最大值和最小值
public class arrayMaxMin{
public static void main(String[] args){
int[] a = {22,33,44,55,66,11,99,88,77}; //声明整数数组a,并赋初值
int max = a[0]; //储存最大的元素,假设第一个元素是最大的
int min = a[0]; //储存最小的元素,假设第一个元素是最小的
System.out.println("数组a的元素包括:");
for(int i = 0;i < a.length;i++){
System.out.print(a[i] + " ");
if(a[i] > max) //判断最大值
max = a[i];
else if(a[i] < min) //判断最小值
min = a[i];
}
System.out.println("\n数组的最大值是:" + max); //输出最大值
System.out.println("数组的最小值是:" + min); //输出最小值
}
}
三、二维数组
虽然使用一维数组可以处理一般简单的数据,但是在实际应用中仍显不足,所以 Java 也提供有二维数组以及多维数组供程序设计人员使用。
1.二维数组的声明与分配内存
二维数组声明的方式和一维数组类似,内存的分配也一样是用 new 这个关键字。其声明与分配内存的格式如下:
数据类型[][] 数组名;
数组名 = new 数据类型[行的个数][列的个数];
同样,可以使用较为简洁的方式来声明数组,其格式如下:
数据类型[][] 数组名 = new 数据类型[行的个数][列的个数];
如果想直接在声明时就对数组赋初值,可以利用大括号完成。只要在数组的声明格式后面再加上所赋的初值即可,如下面的格式:
数据类型[][] 数据名 = {
{第0行初值},
{第1行初值},
…
{第n行初值}
};
注意:用户不需要定义数组的长度,因此在数组名后面的中括号里不必填入任何的内容。此外,在大括号内还有几组大括号,每组大括号内的初值会依序给指定数组的第0/1、…、n 行元素。
下面是关于数组 num 声明及赋初值的例子:
int[][] num = {
{11,22,33,44}, //二维数组第0行的初值赋值
{55,66,77,88} //二维数组第1行的初值赋值
};
语句中声明了一个整形数组 num,数组有 2 行 4 列共 8 个元素,大括号里的几组初值会分别依序指定给各行里的元素存放,num[0][0] 为 11,num[0][1] 为 22。
(1)每行的元素个数不同的二维数组
值得一提的是,Java 在定义二维数组时更加灵活,允许二维数组中每行的元素个数均不相同,这点与其他编程语言不同。例如以下代码:
int[][] num = {
{11,22,33,44,55},
{66,77,88},
{12,34,56,78}
};
(2)取得二维数组的行数与特定行的元素的个数
在二维数组中,若想去的整个数组的行数,或者是某行元素的个数,则可利用 “ .lenght ” 来获取。其语法如下:
数组名.lenght //取得数组的行数
数组名[行的索引].lenght //取得特定行元素的个数
也就是说,若要取得二维数组的行数或者元素的个数,在后面加上索引值 “ .lenght ” 即可。如下所示:
num.lenght; //计算数组num的行数
num[0].lenght; //计算数组num的第1行元素的个数
2.二维数组元素的引用及访问
二维数组元素的输入与输出方式与一维数组相同。
四、多维数组
想要提高数组的维数,只要在声明数组的时候将索引与中括号再加一组即可。以下举三维数组的例子说明:
//三维数组的使用方法,要输出数组的内容需要采用三重循环
public class threeDimensionArray{
public static void main(String[] args){
int sum = 0;
int[][][] a = { //生命数组并设置初值
{{5,1},{6,7}},
{{9,4},{8,3}}
};
//三维数组的输出需要采用三层for循环方式输出
for(int i = 0;i < a.length;i++){
for(int j = 0;j < a[i].lenght;j++){
for(int k = 0;k < a[j].lenght;k++){
System.out.print("a[" + i + "][" + j + "][" + k + "] = ");
System.out.println(a[i][j][k]);
sum+=a[i][j][k];
}
}
}
System.out.println("sum = " + sum);
}
}
五、本文注意事项
1.Java 中的 null 的使用
Java 中变量通常遵循一个原则:先定义,并初始化后,然后再使用。有时候,我们定义一个类型变量,在刚开始的时候,无法给出一个明确的值,就可以用一个 null 来代替。
但是有一点需要注意的是,不可以将 null 赋给基本类型变量(如 int、float、double 等)。
比如:
int a = null; //此行代码是错误的
Object a = null; //此行代码正确,因为这里的Object是一个class类型
2.数组的下标
在使用数组时,值得注意的是,Java 中数组的下标是从 0 开始计数的。
此外,数组的下标不能超过(lenght - 1),否则会产生越界错误,例如,我们定义一个包含 10 个元素的数组 score。
int[] score = new int[10];
那么 score[9] 是我们能使用的最大下标数组元素,而 score[10] 则产生了越界。