3.9数组
3.9.1数组基本使用
数组,英文叫Array,是一种数据结构,是用来存放同一数据类型数值的集合。例如存放30个int型数值、存放100个double型数值等等。
我们知道使用一个变量,需要先声明一个变量,例如:int a;使用数组同样也需要先声明一个数组变量。假设我们要声明一个int类型的数组变量,有2种方式:
int[] a; int b[];
但是一般Java工作者都习惯于第一种方式,因为int[]看起来更像数据类型,后面跟一个变量名。
声明变量,实际上是在内存中给它分配一块空间。但是数组是存放若干个数据,因此还得继续声明它的大小,即存放多少个数据。Java中使用new运算符来操作。像下面这样:
a = new int[30];
我们还可以在声明数组的同时就分配空间:
int[] a = new int[30];
上面这条语句声明int型的数组a可以存放30个int的数值。这样,就会在内存中分配30个连续的空间。
数组大小分配好了以后。我们要访问数组中的某一个元素的话,可以用一个整型的下标(index)来访问。下标是从0开始的,因此上面的数组a的下标是0~29。比如我们要访问第29个元素,那么可以用a[28]。这里需要注意,数组一旦被创建了以后,大小就是固定的。如果下标超出范围,例如访问a[30],程序会报异常,异常一般是:“array index out of bounds”,称做“下标越界”。
给数组的元素赋值就很简单了,就像给一个普通变量赋值一样:
a[22] = 22;
我们还可以在声明数组的时候同时赋值,有两种形式:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = { 1, 2, 3, 4 };
注意第一种形式,[]内不需要指定大小。有的时候,我们还需要知道数组的大小,可以用a.length来获得。例如我们想遍历打印数组的值:
for (int i = 0; i < a.length; i++) { System.out.println(a[i]); }
综合上面的讨论,我们可以归纳一下数组的3要素:
- 声明一个数组,有2种形式,一般采用 “数据类型[] 变量名” 的形式
- 给数组分配大小,用new关键字,形式为 “变量名=new 数据类型[大小]”。一旦分配完大小,数组的大小就固定了,可以用“变量名.length”来获取数组的大小。访问数组的元素用“变量名[下标]”的方式。下标的范围是0~length。如果不在这个范围内,程序会报“下标越界”异常。
- 给数组的元素赋值
访问数组的元素用“变量名[下标]”的方式。下标的范围是0~length。如果不在这个范围内,程序会报“下标越界”异常。
用一张图总结一下:
3.9.2数组的循环
在实际运用中,经常会有遍历数组的需求。上面我们用for演示过遍历数组的情况。事实上,在Java5.0之后,有另外一种for循环的结构,可以非常方便的遍历一个集合中的元素。代码形式为:
for(类型 变量:集合){语句}
我们看一个例子:
int[] a = new int[] { 1, 2, 3, 4 }; for (int i : a) { System.out.println(i); }
运行结果:
1 2 3 4
这种for循环可以理解为“遍历集合中的每一个元素”。
3.9.3数组拷贝
在实际工作中,还会经常碰到需要将一个数组中的全部或部分元素拷贝到另一个元素中的需求。如果是全量拷贝,有一个很简单的办法:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = a;
执行以上代码后,数组b和数组a就一样了。但是这样有一个问题,我们继续编写代码:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = a; b[3] = 33;// 将数组的b的第4个元素赋值为33 System.out.println(a[3]);// 结果数组a的第4个元素也变成33
我们修改数组b的第4个元素,结果数组a的第4个元素也跟着一块修改了。这是为什么呢?这是因为Java中变量的的赋值,是引用赋值,用内存的表现来解释可以一目了然:
把变量a赋值给变量b,实际上b和a将指向同一个内存地址。因此修改b的元素,实际上就是修改内存中的值,这样a的元素自然也就跟着修改了。我们称这种拷贝为“浅拷贝”。如果想要实现另外分配一块内存空间给数组b,有没有办法呢?Java给我们提供了2种方法,一种是用System类的arraycopy方法,还有一种是Java6之后新提供的Arrays类的copyOf方法。具体怎么用?还记得安装JDK的时候,提到过的API文档吗?我们这时候就需要用到它了。(如果不记得了,回到那一节看一遍)。
笔者的API文档的路径是:D:\Java大失叔\Java\jdk-8u261-docs-all\docs,我们找到api目录下的index.html,用浏览器打开,可以看到首页:
左上是所有的包,左下是当前包下的类,右边是当前类的API说明。将来我们会经常用到API文档来查找类的使用说明。我们先来看一下System的arraycopy方法。
System类在java.lang包下,我们定位到System类后,找到arraycopy方法,点击方法名,可以进入该方法的详细说明。我们摘抄方法体:
arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
这个方法的作用就是从源数组src的srcPos下标开始,拷贝length个元素到目标数组dest中,目标数组的起始下标为destPos。我们直接上代码:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = new int[4]; System.arraycopy(a, 0, b, 0, 4); b[3] = 33;// 将数组的b的第4个元素赋值为33 System.out.println(b[3]);// 数组b的第4个元素变成33 System.out.println(a[3]);// 数组a的第4个元素仍然是4
接下来,我们看一下Arrays.copyOf方法,Arrays类在java.util包下,util包提供了很多有用的工具类,Arrays是其中之一,我们会发现有很多copyOf方法,我们找到对应int型的:
copyOf(int[] original, int newLength)
这个方法的作用就是将源数组original的所有元素拷贝到一个新的数组中,可以指定新的数组的大小newLength,然后返回新的数组。如果newLength比源数组大小大,那么新数组多余的元素将被赋值为0,如果newLength比源数组大小小,那么将只拷贝newLength个元素。我们上代码:
int[] a = new int[] { 1, 2, 3, 4 }; int[] b = Arrays.copyOf(a, 5); int[] c = Arrays.copyOf(a, 3); b[0] = 33;// 将数组的b的第4个元素赋值为33 c[0] = 44;// 将数组的b的第4个元素赋值为33 System.out.println(a[0]);// 数组a的第1个元素仍然是4 System.out.println(b[0]);// 数组b的第1个元素变成33 System.out.println(c[0]);// 数组c的第1个元素变成44
3.9.4数组排序
数组的排序也可以用Arrays类的sort方法,我们摘抄方法:
sort(int[] a)
这个方法对数组a进行升序排序。它内部采用的是优化的快速排序算法,这个算法对于大多数的数据集合来说效率都比较高。我们看一下代码:
int[] a = new int[] { 1, 4, 2, 3 }; Arrays.sort(a); for (int i : a) { System.out.println(i); }
运行结果:
1 2 3 4
排序之后,按照升序排列了。
Arrays类还有很多有用的方法,这里就不一一列举了,大家以后如果碰到需要对数组进行某些操作的时候,可以想到来查一下Arrays类,看看有没有对应的方法。
3.9.5多维数组
Java中还支持多维数组,但是其实在实际运用中很少用到,最多也就用一下二维数组,因此这里只粗略的介绍一下二维数组。我们经常用到Excel表格,其实就可以看成一个二维数组,例如:
声明二维数组、分配空间和赋值访问和一维数组类似,我们用代码演示:
int[][] table;// 声明一个二维数组 table = new int[5][4];// 分配空间5行4列 table[0][1] = 12;// 第1行第2列赋值为12
对于赋值,也可以和一维数组一样,在声明的同时就赋值:
int[][] table = new int[][] { { 11, 12, 13, 14 }, { 21, 22, 23, 24 } };// 声明一个二维数组,并赋值 int[][] table2 = { { 11, 12, 13, 14 }, { 21, 22, 23, 24 } };// 声明一个二维数组,并赋值
二维数组其实可以看成是一个一维数组,然后该维度数组的每一个元素又是一个一维数组。用图可以表示如下:
因此,聪明的你可能发现了,Java的二维数组中,数组的length的值是第一维度的大小。并且我们在分配二维数组大小的时候,可以只分配第一维度的大小,然后再给第一维度的数组的每一个元素分配不同的大小,例如:
int[][] table = new int[4][];// 只分配第一位维度的大小为4 table[0] = new int[1];// 给table[0]分配大小为1 table[0][0] = 11; table[1] = new int[] { 21, 22 };// 给table[1]分配大小为2,同时赋值 table[2] = new int[] { 31, 32, 33 };// 给table[2]分配大小为3,同时赋值 table[3] = new int[] { 41, 42, 43, 44 };// 给table[3]分配大小为4,同时赋值
这其实是一个不规则的二维数组。用表格表示如下图: