3.1 数组概述
变量可以理解成为一个“容器”,然后保存可以变化的值。虽然变量的值不是固定的,但变量一次却只能保存一个值。开发程序是通常会使用非常多的变量,如果一个一个的创建变量,工作量大,严重影响效率,于是所有的开发语言都引入了数组这个概念。数组就是具有相同类型的数据的集合,也就是说一个数组可以同时保存多个值。
数组可以根据阶数分为一维数组、二维数组......
3.2一维数组
3.2.1 创建一维数组
数组元素类型决定了数组的数据类型。它可以是Java中任意的数据类型,包括基本数据类型和其他引用类型。数组名字为一个合法的标识符符号“[ ]”指明该变量是一个数组类型变量。 单个 “[ ]”表示要创建的数组是一个一维数组。
声明一维数组有两种方式:
数组元素类型 数组名字[];
数组元素类型[] 数组名字;
int arr[]; // 声明int型数组,数组中的每个元素都是int型数值
double[] dou; // 声明double型数组,数组中的每个元素都是double型数值
声明数组后,还不能访问它的任何元素,因为声明数组只是给出了数组名字和元素的数据类型,要想真正使用数组,还要为它分配内存空间。在为数组分配内存空间时必须指明数组的长度。为数组分配内存空间的语法格式如下:
数组名字 = new 数组元素类型[数组元素的个数];
数组名字:数组变量的名称。
数组元素个数:指定数组中元素的个数,即数组的长度。
为数组分配内存,语法如下:
arr = new int[5]; //数组长度为5
数组的下标是从0开始的。由于创建的数组arr中有5个元素,因此数组中元素的下标为0~4。
在声明数组的同时也可以为数组分配内存空间,这种创建数组的方法是将数组的声明和内存的分配合在一起执行,语法如下:
数组元素类型 数组名 = new 数组元素类型[数组元素的个数];
int month[] = new int[12];
3.2.2 初始化一维数组
数组可以与基本数据类型一样进行初始化操作,也就是赋初值。数组的初始化可分别初始化数组中的每个元素。数组的初始化有以下3种形式;
int a[] = { 1, 2, 3 }; // 第一种方式
int b[] = new int[] { 4, 5, 6 }; // 第二种方式
int c[] = new int[3]; // 第三种方式
c[0] = 7; // 给第一个元素赋值
c[1] = 8; // 给第二个元素赋值
c[2] = 9; // 给第三个元素赋值
数组的初始化就是包括在大括号之内用逗号分开的表达式列表。用逗号“,”分割数组中的各个元素,系统自动为数组分配一定的空间。
3.2.3 获取数组长度
初始化一维数组的时候都会在内存中分配内存空间,内存空间的大小决定了一维数组能够存储多少个元素,也就是数组长度。如果不知道数组是如何分配内存空间的,该如何获取数组长度呢?可以使用数组对象自带的length属性,语法如下:
arr.length
arr :数组名。
length :数组长度属性,返回int值。
3.3 二维数组
如果一维数组中的各个元素仍然是一个一维数组,那么它就是一个二维数组。二维数组常用于表示表,表中的数据以行、列的方式标记。二维数组有两个下标,第一个下标代表元素所在的行,第二个下标代表元素所在的列。
3.3.1 创建二维数组
二维数组可以看作是特殊的一维数组,因此,二维数组有两种声明方式:
数组元素类型 数组名字[][];
数组元素类型[][] 数组名字;
int tdarr1[][];
char[][] tdarr2;
3.3.2 初始化二维数组
二维数组的初始化方法与一维数组类似,也有多种创建方式。但不同的是,二维数组有两个索引(即下标),构成由行列组成的一个矩阵。
二维数组每一个元素也是一个数组,所以第一种直接赋值方式,在大括号内还有大括号,因为每一个元素都是一个一维数组;第二种使用new的方法与一维数组类似;第三种方法比较特殊,在分配内存空间之后,还有两种赋值的方式,给某一行直接赋值一个一维数组,或者给某一行的每一个元素分别赋值。开发者可以根据使用习惯和程序要求灵活选用其中一种赋值方法。
3.4 数组的基本操作
Java提供了很多用于操作数组的API,本节将介绍几个常用的方法。java.util.Arrays是JDK中的数组类,很多操作方法都是由这个类提供的。
3.4.1 遍历数组
遍历数组就是获取数组中的每个元素。通常遍历数组都是使用for循环来实现。通过数组的length属性可获取数组的长度。
3.4.2 填充替换数组元素
数组中的元素定义完成后,可通过Arrays类的静态方法fill()来对数组中的元素进行替换。该方法通过各种重载形式可完成任意类型的数组元素的替换。
(1)对所有元素进行填充替换
fill()方法的第一种重载形式可以将数组中所有的元素都替换掉,该方法语法如下:
Arrays.fill(int[] a , int value)
a:要进行元素替换的数组。
value:要存储数组中所有元素的值。
(2)对指定范围内元素进行替换
fill()方法方法将指定的int值分配给int型数组指定范围中的每个元素。填充的范围从索引fromIndex(包括)一直到索引toIndex(不包括)。如果fromIndex = = toIndex,则填充范围为空。
fill()方法的第二种重载形式可以指定填充或替换的索引范围,该方法语法如下:
Arrays.fill(int[] a, int fromIndex, int toIndex, int value)
a:要进行填充的数组。
fromIndex:要使用指定值填充的第一个元素的索引(包括)。
toIndex:要使用指定值填充的最后一个元素的索引(不包括)。
value:要存储在数组所有元素中的值。
3.4.3 对数组进行排序
通过Arrays类的静态方法 sort()可以实现对数组的排序。sort()方法提供了多种重载方式,可以对任意类型的数组进行升序排序。语法如下:
Arrays.sort(object)//object为数组的名称
JAVA中的String类型数组的排序算法是根据字典编排顺序排序的,因此数字排在字母前面,大写字母在小写字母前面。
3.4.4 复制数组
Arrarys类的copyOf()方法与copyOfRange()方法都可以实现复制数组的功能。copyOf()方法是按长度复制,copyOfRange()方法可以指定复制的索引范围。
(1)copyOf()方法
该方法提供了多种重载形式,会返回与数组类型相同的结果。该方法语法如下:
Arrays.copyOf(arr,int newlength)
arr:要进行复制的数组,数组的类型决定了返回值的类型。
newlength:int型常量,指复制后的新数组的长度。如果新数组的长度大于数组arr的长度,则用默认值填充(整型的默认值为0,char类型的默认值为null);如果复制后的数组长度小于数组arr的长度,则会从数组arr的第一个元素开始截取至满足新数组长度为止。
(2)copyOfRange()方法
该方法也提供了多种重载形式,也会返回与数组类型相同的结果。该方法语法如下:
Arrays.copyOfRange(arr,int formIndex,int toIndex)
arr:要进行复制的数组对象,数组的类型决定了返回值的类型。
formIndex:指定开始复制数组的索引位置。formIndex必须在0至整个数组的长度之间。新数组包括索引是formIndex的元素。
toIndex:要复制范围的最后索引位置。可大于数组arr的长度。新数组不包括索引是toIndex的元素。
3.5 数组排序算法
对数据进行升序或降序排列是日常工作中经常遇到的需求,这就要求开发者必须掌握基本的数据排序算法,本节将介绍冒泡排序、选择排序和Arrays类提供的排序方法。
3.5.1 冒泡排序
冒泡排序是最常用的数组排序算法之一,它排序数组元素的过程通常按照“小数往前放,大数往后放”这样类似水中气泡往上升的动作,所以称作冒泡排序。它以简洁的思想与实现方法而备受青睐,是初学者最先接触的一个排序算法。
(1)基本思路
冒泡排序的基本思路是对比相邻的元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把大的元素移动到数组后面(也就是交换两个元素的位置),这样较小的元素就像气泡一样从底部上升到顶部。
(2)计算过程
冒泡算法由双层循环实现,其中外层循环用于控制排序轮数,总轮数通常等于数组长度减1,因为最后一次循环只剩下一个数组元素,不需要对比。而内层循环主要用于对比数组中每个临近元素的大小,以确定是否交换位置,对比和交换次数以排序轮数而减少。例如,一个拥有6个元素的数组,在排序过程中每一次循环的排序过程和结果如下图所示,绿色的数字处于正在进行比较的状态,蓝色的数字表示处于等待排序的状态,黑色的数字表示已经完成排序的状态。
算法完成第一轮比较后,把最大的元素值95移动到了最下面(相应的比95小的元素向前移动,类似气泡上升),当95下沉到底部之后就不会再参与下一轮的比较。按照这样的逻辑,每一轮比较之后,都会有一个最大元素下沉到底部。 当所有的元素都“沉底”之后,即得到升序排列的数组。
3.5.2 直接选择排序
直接选择排序算法属于选择排序的一种,它的排序速度要比冒泡排序快一些,也是常用的排序算法。
(1)基本思路
直接选择排序的基本思路是先指定一个排序位置(例如数组末尾),让指定位置的元素与其他元素逐一对比,如果知道最大或最小值就交换两个位置的值。
直接选择排序与冒泡排序是有区别的。直接选择排序不是交换相邻元素,而是把满足条件的元素与指定的排序位置交换(如第一个元素与最后一个元素交换),这样排序好的位置逐渐扩大,最后整个数组都成为已排序好的格式。
与冒泡排序相比,直接选择排序的交换次数要少很多,所以速度会快些。
(2)计算过程
以升序排列为例,算法会找出数组中值最大的元素,将值最大的元素与数组最后一个元素交换位置。例如,一个拥有6个元素的数组,在排序过程中每一次比较的过程和结果如图3所示,蓝色的数字表示等待比较的状态,黑色的数字表示已经完成排序的状态。在第一次比较的过程中,先找到了最大值63,再将63这个元素与最后一个元素互换位置, 完成互换之后,63的位置就固定了,不再参与下一次比较。第二次比较过程中,先找到了最大值24,再将24这个元素与当前最后一个元素互换位置,完成互换之后,24的位置就固定了。依此类推,每一次比较之后都会将最大的元素放在数组末尾。
算法比较的次数为数组长度减1,因为最后一次比较时,只剩下两个等待比较的元素,完成这两个元素的比较之后,整个数组就完成了排序。
3.5.3 反转排序
顾名思义,反转排序就是以相反的顺序将原有数组的内容重新排序。反转排序算法在程序开发中也经常使用。
(1)基本思路
反转排序的思路非常简单,也很好理解,其实现思路就是将数组的第一个元素和最后一个元素进行替换,将第二个元素和倒数第二个元素进行替换,以此类推,直到将数组中的所有元素进行替换。
反转排序是对数组两边的元素进行替换,所以for循环只需要循环数组长度的半数次。