5.1 数组概述
数组是最常见的一种数据结构,是同类型的用一个标识符封装到一起的基本类型数据序列或对象序列。可以用一个统一的数组名和下标来唯一确定数组中的元素。实质上,数组是一个简单的线性序列,因此访问速度很快。
数组是具有相同数据类型的一组数据的集合。在程序设计中,可以将这些集合称为数组。数组中的每一个元素具有相同的数据类型。在Java中同样将数组看作一个对象,虽然基本数据类型不是对象,但由基本数据类型组成的数据确是对象。
5.2 一维数组
一维数组实质上是一组相同类型数据的线性集合,当在程序中需要处理一组数据,或者传递一组数据时,可以应用这种类型的数组。
5.2.1 创建一维数组
数组作为对象允许使用new关键字进行内层分配。在使用数组之前,必须首先定义数组变量所属的类型。一维数组的创建有两种形式。
1.先声明,再用new关键字进行内存分配
声明一维数组有下列两种方式:
数组元素类型 数组名字【】;
数组元素类型【】数组名字;
数组元素类型决定了数组的数据类型,它可以是Java中任意的数据类型,包括简单类型数据,包括简单类型和组合类型。数组名字为一个合法的标识符,符号“【】”指明该变量是一个数组类型变量。单个“【】”表示要创建的数组是一个一维数组。
声明一维数组,代码如下:
int arr[]; //声明int型数组,数组中每一个元素都是int型数值
声明数组后,还不能立即访问它的任何元素,因为声明数组只是给出了数组名字和元素的数据类型,要想真正使用数组,还要为它分配内存空间。在为数组分配内存空间时必须指明数组长度。为数组分配内存空间的语法格式如下:
数组名字=new数组元素的类型[数组元素的个数];
数组名称:被连接到数组变量名称。
数组元素的个数:指定数组中变量的个数,即数组的长度。
通过上面的语法可知,使用new关键字为数组分配内存时,必须指定数组元素的类型和数组元素的类型和数组元素的个数,即数组长度。
为数组分配内存,代码如下:
arr=new int[5];
以上代码表示要创建一个含有5个元素的整形数组,并且将创建的数组对象赋给引用变量arr,即通过引用变量arr来引用这个数组,如下图所示:
arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
一维数组的内存模式 |
在上图中,arr为数组名称,方括号“[]”中的值为数组下标。数组通过下标来区分数组中不同的元素。数组下标是从0开始。由于创建的数组aar中有5个元素,因此数组中元素下标为0~4.
2.声明的同时为数组分配内存
这种创建数组的方法是将数组的声明和分配合在一起执行。语法如下:
数组元素的类型 数组名 =new数组元素的类型[数组元素的个数];
声明并为数组分配内存,代码如下:
int month[]=new int[12]
上面的代码创建数组month,并指定了数组长度为12的。这种创建数组的方法也是Java程序编写过程中普遍的做法。
5.2.2 初始化一维数组
数组与基本数据类型一样可以进行初始化操作。数组的初始化可分别初始化数组中的每个元素。数组的初始化有以下两种方式:
int arr[]=new int[]{1,2,3,5,25};
int arr2[]={34,23,12,6};
从中可以看出,数组的初始化就是包括在大括号之内用逗号分开的表达式列表。用逗号(,)分割数组中的各个元素,系统自动为数组分配一定的空间。用第一种初始化方式,将创建5个元素的数组,其元素依次为1,2,3,5,25。第二种初始化方式,会创建4个元素的数组,其元素依次为34,23,12,6。
5.2.3 使用一维数组
在Java集合中,一维数组是常见的一种数据结构。
例题5.1 使用一维数组输出1~12月每个月份的天数
代码为:
public class GetDay { //创造类
public static void main(String[] args) { //主方法
int day[] = new int[] {31,28,31,30,31,30,31,31,30,31,30,31}; //创造并初始化一维数组
for(int i = 0;i<12;i++) { //利用循环将信息输出
System.out.println((i+1)+"月有"+day[i]+"天;"); //输出每月的天数
}
}
}
结果为:
5.3 二维数组
如果一维数组中各个元素仍然是一个数组,那么它就是一个二维数组。二维数组常用于表示表,表中的信息以行和列的形式组织,第一个下标代表元素所在的行,第二个下标代表元素所在的列。
5.3.1 创建二维数组
二维数组可以看作是特殊的一维数组,因此二维数组的创建同样有两种方式。
先声明,再用new关键字进行内存分配
声明二维数组的语法如下:
数组元素的类型 数组名字[][];
数组元素的类型[][] 数组名字;
声明二维数组,代码如下:
int a[][];
同一维数组一样,二维数组在声明时也没有分配内存空间,同样要使用new关键字来分配内存,然后才可以访问每个元素。对于高维数组,有两种为数组分配内存的方式。
第一种内存分配方式是直接为每一维分配内存空间,代码如下:
a==new int[2][4]
上述代码创建了二维数组a,二维数组a中包括两个长度为4的数组,内存分配图如下所示。
第二种内存分配方式是分别为每一维分配内存,代码如下:
a = new int[2][];
a[0]=new int[2];
a[1]=new int[3];
上述代码创建了二维数组a,但是只声明了a第一维的长度,也就是“行数”,第二维的长度也就是“列数”,则是为每一行单独声明的,因此创建的数组a是“不定长数组”,其内存分配如下图所示。
声明的同时为数组分配内存
第二种创建方式与第一种实现的功能相同,只不过声明与赋值合并到同一行代码中。例如,创建一个2行4列的二维数组,代码如下:
int a=new int[2][4]
5.3.2 初始化二维数组
二维数组的初始化与一维数组初始化类似,同样可以使用大括号完成。语法如下:
type arrayname[][]={value1,value2,……valuen};
type:数组数据类型。
arrayname:数组名称,一个合法的标识符。
value:二维数组中各元素,都代表一个一维数组。
初始化二维数组,代码如下:
int myarr[][]={{12,0},{45,10}};
初始化二维数组后,要明确数组的下标都是0开始。例如,上面的代码中myarr[1][1]的值10。
int型二维数组是以int a[][]来定义的,所以可以直接给a[x][y]赋值。例如,给a[1]的第2个元素赋值的语句如下:
a[1][1]=20
5.3.3 使用二维数组
例5.2 输出一个3行4列且所有元素都为0的矩阵
public class Matrix { //创造类
public static void main(String[] args) { //主方法
int a[][] = new int[3][4]; //定义二维数组
for(int i = 0;i<a.length;i++) {
for(int j = 0;j<a[i].length;j++) { //循环遍历数组中每一个元素
System.out.print(a[i][j]); //将数组中的元素输出
}
System.out.println(); //输出空格
}
}
}
结果为:
5.4 数组的基本操作
5.4.1 遍历数组
遍历数组就是获取数组中的每一个元素。通常遍历数组都是使用for循环来实现。遍历二维数组需使用双层for循环,通过数组的length属性可获得数组的长度。
例5.3 呈梯形输出二维数组中的元素
代码如下:
public class Trap { //创造类
public static void main(String[] args) { //主方法
int b[][] = new int[][] {{ 1 },{2,3},{4,5,6}}; //定义二维数组
for(int k = 0;k < b.length;k++) {
for(int c = 0;c<b[k].length;c++) { //循环遍历二维数组中每一个元素
System.out.print(b[k][c]); //将数组中的元素输出
}
System.out.println( ); //输出空格
}
}
}
结果为:
结果为:
在遍历数组时,使用foreach语句可能会更简单。
例5.4 使用foreach语句遍历二维数组
代码为:
public class Tautog { //创造类
public static void main(String[] args) { //主方法
int arr2[][] = {{4,3},{1,2}}; //定义二维数组
System.out.println("数组中的元素是:"); //提示信息
int i = 0; //外层循环计数器变量
for(int x[]:arr2) { //外层循环变量为一维数组
i++; //外层计数器递增
int j = 0; //内层循环计数器变量
for(int e : x) { //循环遍历每一个数组元素
j++; //内层计数器递增
if(i==arr2.length&&j==x.length) { //判断变量是二维数组中最后一个元素
System.out.print(e); //输出二维数组的最后一个元素
}else //如果不是二维数组中的最后一个元素
System.out.print(e+"、"); //输出信息
}
}
}
}
结果为:
5.4.2 填充替换数组元素
数组中的元素定义完成后,可通过Arrays类的静态方法fill()来对数组中的元素进行替换。该方法通过各种重载形式可完成对任意类型的数组元素的替换。
fill(int[]a,int value)
该方法可将指定的int值分配给int型数组的每个元素。语法如下:
fill(int[]a,int value)
a:要进行元素替换的数组
value:要存储数组中所有元素的值。
例5.5 使用fill(方法填充数组元素)
代码为:
import java.util.Arrays; //导入java.util.Arrays类
public class Swap { //创造类
public static void main(String[] args) { //主方法
int arr[] = new int[5]; //创建int型数组
Arrays.fill(arr,8); //使用同一个值对数组进行填充
for(int i = 0;i<arr.length;i++) { //循环遍历数组中的元素
System.out.println("第"+i+"个元素是:"+arr[i]); //将数组中的元素依次输出
}
}
}
结果为:
fill(int[]a,int fromIndex,int toIndex,int value)(前改索引后不改索引)
该方法将指定的int值分配给int型数组指定范围中的每个元素。填充的范围从索引(下标)fromIndex(包括)一直到索引toIndex(不包括)。如果fromlndex==toIndex,则填充范围为空。语法如下:
fill(int[]a,int fromIndex,int toIndex,int value)
a:要进行填充的数组。
fromIndex:要使用指定值填充的第一个元素的索引(包括)。
toIndex:要使用指定值填充的最后一个元素的索引(不包括)。
value:要分配给数组指定范围中的每个元素的值。
例5.6 使用fill()方法替换数组中的元素
代码为:
import java.util.Arrays; //导入java.util.Arrays类
public class Displace { //创造类
public static void main(String[] args) { //主方法
int arr[] = new int[]{45,12,2,10}; //定义并初始化int型数组arr
Arrays.fill(arr,1,2,8); //使用fill方法替换数组指定范围内的元素
for(int i = 0;i < arr.length;i++) { //循环遍历数组中的元素
System.out.println("第"+i+"个元素是:"+arr[i]); //将数组中的每个元素输出
}
}
}
结果为:
5.4.3 对数组进行排序
通过Arrays类的静态方法sort()可以实现对数组的排序。sort()方法提供了多种重载形式,可对任意类型的数组进行升序排序。语法如下:
Arrays,sort(object)
例5.7 使用sort()方法将数组排序后输出
代码为:
import java.util.Arrays; //导入java.util.Arrays类
public class Taxis { //创造类
public static void main(String[] args) { //主方法
int arr[]= new int[] {23,42,12,8}; //声明数组
Arrays.sort(arr); //将数组进行排序
for(int i = 0;i<arr.length;i++ ) { //循环遍历排序后的数组
System.out.println(arr[i]); //将排序后数组中的各个袁旭输出
}
}
}
结果为:
上述实例是对整型数组进行排序。Java中的String类型数组的排序算法是根据字典编排顺序排序的,因此数字排在字母前面,大写字母排在小写字母前面。
5.4.4 复制数组
Arrays类的copyOf()方法与copyOfRange()方法可以实现对数组的复制。copyOf()方法是复制数组至指定长度,copyOfRange()方法则将指定数组的指定长度复制到一个新数组中。
copyOf()方法
该方法提供了多种重裁形式,用于满足不同类型数组的复制。语法如下:
copyOf(arr,int newlength)
arr:要进行复制的数组。
newlength:int型常量,指复制后的新数组的长度。(空位补0,溢出去掉)
例5.8 复制数组
代码为:
import java.util.Arrays; //导入java.util.Arrays类
public class Cope { //创造类
public static void main(String[] args) { //主方法
int arr[] = new int[] {23,42,12}; //定义数组
int newarr[] = Arrays.copyOf(arr, 5); //复制数组arr
for(int i = 0;i < newarr.length;i++) { //循环遍历复制后的新数组
System.out.println(newarr[i]); //将新数组输出
}
}
}
结果为:
copyOfRange()方法(前在后不在)
该方法同样提供了多种重裁形式,语法如下:
copyOfRange(arr,int formIndex,int toIndex)
arr:要进行复制的数组对象。
formIndex:指定开始复制数组的索引位置。formIndex必须在0至整个数组的长度之间。新数组包括索引是formIndex的元素。
toIndex:要复制范围的最后索引位置。可大于数组arr的长度。新数组不包括索引是toIndex的元素。
例5.9按照索引复制数组
代码为:
import java.util.Arrays; //导入java.util.Arrays类
public class Repeat { //创造类
public static void main(String[] args) { //主方法
int arr[] = new int[] {23,43,12,84,10}; //定义数组
int newarr[] = Arrays.copyOfRange( arr, 0, 3); //复制数组
for(int i = 0;i<newarr.length;i++) { //循环遍历复制后的新数组
System.out.println(newarr[i]); //将新数组中的每个元素输出
}
}
}
结果为:
5.4.5 查询数组
Arrays类的binarySearch()方法,可使用二分搜索法来搜索指定数组,以获得指定对象,该方法返回要搜索元素的索引值。binarySearch()方法提供了多种重裁形式,用于满足各种类型数组的查找需要。
binarySearch(Object[]a,Object key)(先排序,后查询)
语法如下:
a:要搜索的数组。
key:要搜索的值。
如果key包含在数组中,则返回搜索值的索引;否则返回-1或“-”(插入点)。插入点是搜索键将要插入数组的那一点,即第一个大于此键的元素索引。
查询数组元素,代码如下:
int arr[]=new int{4,25,10}; //创建并初始化数组
Arrays.sort(arr); //将数组进行排序
int index=Arrays.binartSearch(arr,8);
上面的代码中变量index的值是元素“8”在数组arr中索引在0~1内的索引位置。由于在指定的范围并不存在元素“8”,index的值是“-”(插入点)。如果数组进行排序,元素“8”应该在“25”的前面,因此插入点应是元素“25”的索引值为2,所以index的值是-2。
如果数组中的所有元素都小于指定的键,则为a.length(注意,这保证了当且仅档此键被找到时,返回的值将大于等于0)。
例5.10 查找元素在数组中的索引位置
代码如下:
import java.util.Arrays; //导入java.util.Arrays类
public class Reference { //创造类
public static void main(String[] args) { //主方法
int ia[]= new int[] {1,8,9,4,5}; //定义int型数组
Arrays.sort(ia); //将数组进行排列
int index = Arrays.binarySearch(ia, 4); //查找数组ia中元素4的索引位置
System.out.println("4的索引位置是:"+index); //将索引输出
}
}
结果为:
binarySearch(Object[]a,int fromIndex,int toIndex,Object key)
该方法在指定的范围内检查某一元素。语法如下:
binarySearch(Object[]a,int fromIndex,int toIndex,Object key)
a:要进行检索的数组。
fromIndex:指定范围的开始处索引(包含)。
toIndex:指定范围的结束处索引(不包含)。
key:要搜素的元素。
在使用该方法前,同样要对数组进行排序,这样才能获得获得准确的索引值。如果要搜索的元素key在指定的范围内,则返回搜索键的索引;否则返回-1或“-”(插入点)。如果范围中的所有元素都小于指定的键,则插入点为toIndex(注意,这保证当且仅档此键被找到时,返回的值将大于等于0)。
例5.11在指定范围内查找元素在数组中的索引位置
代码为:
import java.util.Arrays; //导入java.util.Arrays类
public class Rakel { //创造类
public static void main(String[] args) { //主方法
String str[] = new String[] {"ab","cd","ef","yz"}; //定义Steing型数组str
Arrays.sort(str); //将数组进行排序
int index = Arrays.binarySearch(str, 0,2,"cd"); //在指定范围内搜索元素“cd”的索引位置
System.out.println("cd的索引位置是:"+index); //将索引输出
}
}
结果如下:
5.5 数组排序算法
5.5.1 冒泡排序
冒泡排序是最常用的数组排序算法之一,它排序数组元素的过程总是将较小的数往前放、较大的数往后放,类似水中气泡往上升的动作,所以称为冒泡排序。
1.基本思想
冒泡排序的基本思想是对比相邻的元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把较大的元素移动到数组后面(也就是交换两个元素位置),这样较小的元素就像气泡一样从底部上升到顶部。
2.算法实现
例5.12 冒泡排序
代码为:
public class BubbleSort { //创造类
public static void main(String[] args) { //主方法
int[]array = {63,4,24,1,3,15}; //创建一个数组,元素是乱序的
BubbleSort sorter = new BubbleSort(); //创建冒泡排序类的对象
sorter.sort(array); //调用排序方法,对数组排序
}
public void sort(int[]array) {
for(int i = 1;i<array.length;i++) {
//比较相邻两个元素,较大的元素往后冒泡
for(int j = 0;j <array.length-i;j++) {
if(array[j]>array[j+1]) {
int temp = array[j]; //把第一个元素值保存到临时变量中
array[j]=array[j+1]; //把第二个元素值保存到第一个元素单元中
array[j+1]=temp; //把临时变量(第一个元素原值)保存到第二个元素单元中
}
}
}
showArray(array); //输出冒泡排序后的数组元素
}
public void showArray(int[]array) {
for(int i:array) { //遍历数组
System.out.print(">"+i); //输出每个数组元素值
}
System.out.println();
}
}
结果为:
5.5.2 直接选择排序
基本思想
直接选择排序属于的基本思想是将排序位置元素与其他数组元素分别对比,如果满足条件就交换元素值。
算法演示
例5.13直接选择排序
public class SelectSort { //创造类
public static void main(String[] args) { //主方法
int[]array = {63,4,24,1,3,15}; //创建一个数组,元素是乱序的
SelectSort sorter = new SelectSort(); //调用排序对象方法,对数组排序
sorter.sort(array);
}
public void sort(int[] array) {
int index;
for(int i = 1;i<array.length;i++) {
index = 0;
for(int j =1;j<=array.length-i;j++) {
if(array[j]>array[index]) {
index = j ;
}
}
//交换在位置array.length-i和index(最大值)上的两个数
int temp = array[array.length-i]; //把一个元素值保存到临时变量中
array[array.length-i] = array[index]; //把二个元素值保存到第一个元素单元中
array[index] = temp; //把临时变量(第一个元素原值)保存到第二个元素单元中
}
showArray(array); //输出直接选择排序后的数组元素
}
public void showArray(int[]array) {
for(int i:array) { //遍历数组
System.out.print(">"+i); //输出每个数组元素值
}
System.out.println();
}
}
结果为:
5.5.3 反转排序
基本思想
反转排序的基本思想比较简单,也很好理解,其实现思路就是把数组最后一个元素与第一个元素替换,倒数第二个元素与第二个元素替换,以此类推,直到把所有数组元素反转替换。
算法实现
例5.14 反转排序
代码为:
public class ReverseSort {
public static void main(String[] args) {
int[]array = {10,20,30,40,50,60}; //创建一个数组
ReverseSort sorter = new ReverseSort(); //创建反转排序类的对象
sorter.sort(array); //调用排序对象方法,将数组反转
}
public void sort(int[]array) {
System.out.println("数组原有内容:");
showArray(array); //输出排序前的数组元素
int temp;
int len = array.length;
for(int i = 0;i<len/2;i++) {
temp = array[i];
array[i] = array[len-1-i];
array[len-1-i]=temp;
}
System.out.println("数组反转后内容:");
showArray(array); //输出排序后的数组元素
}
public void showArray(int[] array) {
for(int i:array) { //遍历数组
System.out.print("\t"+i); //输出每个数组元素值
}
System.out.println();
}
}
结果为: