【因为每一章都篇幅比较长(10多页,难免有的地方会写错字,如发现请指正,本人不胜感激)】
数组是一种数据结构,它的功能是存储同一类型的值,本章介绍数组的声明,创建,初始化以及其实现机制
5.1 数组的声明与创建
【数组本身是对象,这是java数组与其他语言最大的差别】
【访问数组对象必须通过数组对象,数组对象是真正存放元素的仓库,而数组对象引用是唯一访问仓库的方式】
数组中的基本元素可以是基本数据类型,也可以是引用类型。
5.1.1 声明数组引用
需提供数组将要保存元素的数据类型以及该数组的维度两方面信息,维数通过方括号对数来指出,方括号对可以位于数组的左边,也可以位于数组的右边 eg:
int k[];//声明int类型数组
String[] s;//声明string类型数组,最佳声明方式
int[][][] b;//声明int型的三维数组引用b b为数组引用
又得数组在声明的时候可以指定数组长度,eg:int[5] i;但是java中不可以在声明是指定数组长度
5.1.2 创建数组对象
最常用方式:使用new关键字,和创建类对象一样
基本语法:
new 元素类型[第一维数组][第二维数组]……
提示:创建数组时一定要给出第一位数组所需要的长度,否则系统无法为其分配内存空间,至于第二维长度可以给可以不给;eg:
int[] k;//声明int型一维数组引用K
k=new int[3];// object=new int[3]; 将引用k指向长度为5的int型的一位数组对象
String[][] s=new String[5][];
int[] i=new int[]{1,2,3};
这样就可以通过数组引用对其指向的数组对象进行操作了,eg:访问元素,获取长度等,小心数组越界问题,eg:int[2]表示只能有两个元素,0和1;
与其他语言一样,java数组一旦创建,其大小容量不能更改,为了解决数组变长问题,在java中数组的引用的声明和数组对象的创建时分开的,eg:
5.2 java中数组的实现机制
【java中的数组本质是一个对象,他只能通过指向其的引用才能对其访问】
1、基本数据类型的数组实现情况:(数组中存放数组对象)
以上代码的实现机制如下:
1》、图中用椭圆表示数组对象,K则是指向数组对象的引用
2》、数组1,2,3,默认初始值为0
2、引用类型的数组——String是引用类型。
【数组中存放的并不是String对象,而是指向String对象的引用】eg:
String[] s=new String[3];
s[0]="C++";
s[1]="python";
s[2]="java";
实现机制:
多维数组的从逻辑上看可以看成数组的数组,而java中的多维数组与String一样都是对象,所以在java中n维数组实际上是一维的n-1维数组引用型数组,即:n维数组是由n层的一位数组搭建而成的;
java中实质只有一维数组,无论多少维都是由一维数组组成:
int[][] k;
k=new int[2][3];
k[0][0]=3;//k[0][1],k[0][2]都是0
k[1][2]=4;
从图中得到:
1)k这个二维数组引用并没有指向二维数组对象,而是指向一维数组对象;
2)K所指向的一维数组对象的元素又是一维数组,其元素是int型
3)二维数组只是数组中的数组,可看做是一堆的对象数组,但每一个元素保存不是其他引用,而是一维数组对象的引用
可以自定义
double[][] d;
d=new double[2][];
d[0]=new double[3];
d[1]=new double[4];
System.out.println(d.length);//注意这里的长度不是7,而是一维数组的长度
System.out.println(d[0].length);//3
System.out.println(d[1].length);//4
5.3 数组的初始化
5.3.1 默认初始化
基本数据类型数组默认值
提示‘\u0000’是不可见字符
对于引用类型数组,无论其元素是何种类型,初值都是null eg:
5.3.2 利用循环初始化
eg:
String[] s=new String[3];
int[] a=new int[2];
for(int i=0;i<s.length;i++)
{
s[i]=""+i;//没有“”会报错,或者是i+""都可以
System.out.print("s["+i+"]="+s[i]+" ");
}
System.out.println("----");//打印换行
for(int i=0;i<a.length;i++)
{
a[i]=i*5;
System.out.print("a["+i+"]="+a[i]+" ");
}
运行结果:
java SE5.0以后,java中增强了for循环对数组和集合的处理能力即 增强for循环
基本语法为:
for(定义暂存变量标识:需要循环的数组引用标识){语句系列}
eg:以上代码更新为:
int[] b=new int[]{0,5,10};
for(int i:b)
{
System.out.print("b["+i+"]="+i+" ");
}
运行结果:
1、增强for循环可以用来依次处理数组中的每一个元素,而不必为下标分心
2、不希望遍历整个数组,或在循环体中需要操作下标值等,还是得需要传统for循环
java中的增强for类似于C#中的foreach,只是将in变为了: eg:
foreach (KeyValuePair<string, string> kv in ascdic)
{
Console.WriteLine("方法一升序以后的key列表为:" + kv.Key + ", value为:" + kv.Value);
}
5.3.3 枚举初始化
使用场景:
1、初始值与默认值不同;//否则用5.3.1方法
2、没有明显规律;//否则用5.3.2方法
3、数量不多
两种语法:
基本语法
省略语法
1、基本语法
创建数组对象的时候逐一列举除所有元素的初始值
基本语法:数组类型[ ] 数组引用标识符=new 数组类型[ ]{第一个元素值,第二个元素值…..}
提示:在采用普通数组创建对象的时候需要制定数组长度,但这种不可以给出数组长度,其长度是由列出来的数组个数决定的。
2、省略语法因(只有枚举有)
基本语法中的“new 数组类型[ ]”没有什么用,还显得累赘,所以java还提供了省略语法
数组类型[ ] 数组引用标识付={第一个元素,第二个元素。。。。。。}eg:
int[] b=new int[]{0,5,10};//未省略
int[] c={2,4,7,8};//省略语法
String[] skyColor={"blue","34","green","beautiful"};
int[][] d={{2,5,7},{3,4},{1,7,9},{0,6,7}}; //多维数组枚举
5.4 数组的相互赋值
实际上是数组引用间的相互赋值,数组是对象,对象时“看不见”的,只有对象引用可以拿在“手中”
5.4.1 基本类型数组赋值规则
数组的位数要相等。
数组元素的类型要完全相同。eg
5.4.2 引用型数组赋值规则:
数组维数要相等;
数组元素类型要兼容;eg:
String[][][][] s4=new String[2][][][];
String[][][] s3=new String[3][][];
String[][] s2=new String[4][];
Object[] o=new Object[3];
s3=s4[0];
s2=s3[3];
o=s[2];//错误,不兼容的类型
5.5 数组的常用操作
5.5.1 数组复制
java中将一个数组引用赋给另一个数组引用后,这两个数组引用指向的是同一个数组对象 如下图:
这样必然会导致对任何一个引用对数组对象进行操作,都会导致其他指向此数组对象的引用感觉到变化,因为其引用的是同一个对象;这与基本数据类型的变量赋值不同,基本百年来那个赋值不会相互影响。eg:
由于存在上述问题,在实际开发中需要经常拷贝数组对象,然后在拷贝上操作,才能不影响原数组,所以java中提供了数组拷贝的工具方法,如下表所列:
public static void arrayCopy(Object src,int srcPos,Object dest,int destPos,int length)
{
//src:源数组引用
//srcPos:拷贝的起始下标
//dest:与src的同类型引用,指向目标数组
//destPos:目标数组放置元素的起始下标
//length:拷贝元素个数【小于等于源数组长度】
//数组越界时会抛出:IndexOutOfBoundsException
}
public static String[] copyOf(xxx[] original,int newLength)
{
//XXX[],表示任意类型
//original:源数组引用,即指向原数组
//newLength:拷贝数组的长度,若拷贝长度大于源数组长度会用0或null填充,反之,截取源数组一部分拷贝到新数组中,返回值为指向数组的引用
}
public static int[] copyOfRange(xxx[] original,int from,int to)
{
//同上xxx[],表示任意类型
//original:源数组引用,即指向原数组
//from:开始位置
//to:结束位置
//返回值为指向新数组的引用
}
使用方法:
//类前面加上:import java.util.Arrays;
//创建数组
int[] a={0,1,2,3,4,5,6,7,8};
int[] dest1=new int[9];
//arraycopy()方法拷贝数组
System.arraycopy(a, 0, dest1, 0, 9);//直接调用此方法,不用写b=,因为返回void,所以不能赋值
dest1[2]=66;
//copyOf()方法拷贝数组
int[] dest2=Arrays.copyOf(a, 7);
dest2[3]=99;
//copyOfRange方法拷贝数组
int[] dest3=Arrays.copyOfRange(a,3,11);
dest3[5]=88;
for(int i:a)
{
System.out.print("源数组:"+i+" ");
}
System.out.println("----换行-----");
for(int i:dest1)
{
System.out.print("1的新数组:"+i+" ");//修改a[1]的值
}
System.out.println("----换行-----");
for(int i:dest2)
{
System.out.print("2的新数组:"+i+" ");//修改a[4]的值,并去掉了最后两个数
}
System.out.println("----换行-----");
for(int i:dest3)
{
System.out.print("3的新数组:"+i+" ");//修改了a[6]的值,并去掉前四个数,增加了两个数
}
运行结果:
只有copyOf和copyOfRange方法的目标数组可以根据拷贝方法参数设定,arraycopy()方法中的参数length要小于等于源数组长度,不然抛异常 eg:
5.5.2 数组排序
排序方法介绍:
这两个方法也可以对引用型数组进行排序,但要求所在类的对象必须有特定的自然顺序,也就是说引用所在类必须实现了Comparable接口
public static void sort(xxx[] a)
{
//此处的xxx[]为,任意类型
//对指定数组排序,排完结果仍然放在 原来的数组中
}
public static void sort(xxx[] a,int fromIndex,int toIndex)
{
//此处的xxx[]即,任意类型
//对指定区间进行排序
//排完结果仍然放在 原来的数组中
}
使用:
//创建数组
int[] a={3,5,6,9,0,2};
int[] b=Arrays.copyOf(a, 7);//复制数组
for(int i:b)
{
System.out.print("排序前:"+i+" ");
}
System.out.println();
System.out.println("开始排序-----------");
Arrays.sort(b);
int[] c=Arrays.copyOf(a, 5);
Arrays.sort(c,1,5);
for(int i:b)
{
System.out.print("全部排序后:"+i+" ");
}
System.out.println();
System.out.println("-----指定区间排序排序-----------");
for(int i:c)
{
System.out.print("除去第一个数排序结果:"+i+" ");//第一个数3没有参与排序,然后将五个数遍历出来
}
运行结果:
提示:对对象引用型数组进行排序还有一种特定方法,其方法签名位:
public static void sort(Object[] a,Comparator c)
{
//a为要排序数组对象的引用
//c为指定的比较器,Comparator这么写会报错
}
5.5.3 搜索指定元素
常用的搜索方法:
public static int binarySearch(xxx[] a,xxx key)
{
//xxx表示,任意类型
//从数组中搜索第一个指定值的位置,key是要找的元素
//a指被搜索的数组
//返回值为搜索到位置的索引,没有找到则返回负数
}
public static int binarySearch(xxx[] a,int fromIndex,int toIndex,xxx key)
{
//xxx表示,任意类型
//fromIndex、toIndex为搜索区间
//返回值为搜索到位置的索引,没有找到则返回负数
}
这两个方法都是基于二元搜索算法的,要求被搜索的数组是已经排好序,可以在搜索前用sort系列方法排序
使用方法:
int[] a={1,3,4,5,6,8,9,12,14,15};
int indexall=Arrays.binarySearch(a, 6);//4
int indexpart=Arrays.binarySearch(a, 8, 9, 8);//2
System.out.println("整个数组中6的位置为:"+indexall);
System.out.println("截取的数组中8的位置为:"+indexpart);
运行结果:
提示:对于对象引用数组的元素搜索还有特定方法,方法签名为:
public static int binarySearch(Object[] a,Object key,Comparator c)
{
//a为要搜索对象数组的引用
//key为要搜索的键
//c为知道那个的比较器
//注意,数组比较的时候使用的哪个比较器,搜索的时候还是使用同样的比较器,否则可能会产生错误结果
}
5.5.4 比较数组中的元素
实际开发中会需要比较两个数组中的元素值是否相等,只需要调用Arrays类中的equals方法即可。eg:
int[] a={1,3,4,5,6,8,9,12,14,15};
int[] b={1,2,4,5,6,8,9,12,14,15};
//比较两个数组元素
boolean flag=Arrays.equals(a, b);
System.out.println("数组a与数组b的元素比较结果为:"+((flag)?"两个数组相等":"两个数组不相等"));
运行结果为:
说明:equals方法的参数有两个,分别是参加比较的数组的引用,返回值是boolean型的,true表示相同,false表示不同
5.6 关于args[ ]
其实Main是一个特定方法,是java执行的入口点,是加载java程序后由java的运行时系统自动调用的。
提示:main方法是系统自动调用的,写错就会变成普通的方法,执行时会报错。
而“String[] args”表示main方法的入口参数,是一个字符串数组,此字符串数组中的元素就是在执行程序时输入的命令行参数,有几个参数数组的长度就是几。输入命令行参数的格式如下:
java 类名 参数1 参数2 参数3
说明:参数间用空格作分隔符 eg:
public static void main(String[] args)
{
if(args.Length<2)
{
System.out.println("命令格式不对,在执行时请输入两个参数!");
}
else
{
System.out.println("接收的参数为:");//打印所有的参数内容
for(String s:args)
{
System.out.println(s + "");
}
}
}
提示:如果在应用程序中要使用命令行参数,在使用前一定要加上类似if判断那样的
容错判断代码,防止用户在使用程序时没有输入足够的参数,导致程序出错。
运行结果:
在没有输入参数的情况下,入口参数args的值为null,null表示没有数组,但其实是用户就算没有输入任何命令行参数,系统一样也会自动创建一个String数组对象,将此对象的引用传给args,只不过这个数组对象很特殊,其长度为0。eg:
public static void mian(String[] args)
{
System.out.println("args:" + args);
System.out.println("args数组的长度为:" + args.Length);
}
运行结果:
从图中可以看出,args的值并不是null,说明其指向了一个数组对象,但是特殊的是,数组对象的长度为0;
提示:允许长度为0的数组存在,是java的一个特色,许多其他语言没有。
第五章完!