------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、数组的定义
数组是同一类型数据的集合,相当于一个容器。数组可以自动给它里边的元素从0开始编号,方便操作这些元素。
数组定义的格式
第一种:
元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
例,
int[] x=new int[3];
这段代码在内存中的表现形式:当程序读到“=”左边,就在栈中定义一个局部变量x,读到右边就通过“new”在堆中创建一个实体的数组空间,这个空间又分为三块区域,每块区域的编号分别是0、1、2,且每块区域都有一个默认值0(不同的数据类型默认值不同,float默认值为0.0f,boolean默认值为false)。同时把这块实体的首地址值(存放位置)赋给x,从而x就指向(引用)了这个数组。
而当x=null时,就会清楚赋给些的地址值,x也就不再指向堆中的数组,那麽此时堆中没有被引用的实体数组就会被虚拟机认定为垃圾,但是不会被立即清理,而是在一个不定时的时间内启动一个垃圾回收机制而被清除
第二种:
元素类型[] 数组名 = new 元素类型[]{元素,元素,元素,,,,,};
例,
int[] x=new int[]{1,2,3};或
int[] x={1,2,3};
这种方式定义的数组,直接被赋予了元素即大括号内的值,每个元素之间用“,”隔开,注意中括号内是没有写数组长度的。
第二种格式适用于,当我们明确数组内元素的内容时使用,而如果元素的内容我们不明确,那么我们需要用第一种格式,然后再为元素分别赋值。例,
int[] x = new int[5];
x[0] = 12;
x[1] = 3;
二、操作数组时的常见异常
1、数组角标越界异常
例,
int[] x=new int[5];
System.out.println(x[5]);
我们知道,当数组中有五个元素是,他们的角标是0~4,没有5号角标。此时再进行编译时是不会发现错误的,因为编译只检查语法错误,这段代码并不存在语法错误。而在运行时,读到这段代码,开始创建实体数组并操作“5”号角标,此时就会报错:“ArrayIndexOutOfBoundsException: 5” 。意思是,5号角标越界异常,找不到5号角标。这就意味着,我们在操作数组时,访问到了数组中不存在的角标。
2、空指针异常
例,
int[] x=new int[5];
x=null;
System.out.println(x[3]);
当“x=null”是,在创建实体数组是赋给x的数组地址就会被消除,即x不再指向数组。这种情况下,编译也是不会发现错误,但是运行时会报错:“NullPointerException”即空指针异常。也就是说,x引用已经不再指向任何实体数组值为空的时候,而我们还在用它在操作数组中的元素,就会报空指针异常了。
三、 数组的常见操作
1、获取数组中的元素
首先,定义一个数组:
int[] arr=new int[]{12,33,5,66,4};
如果我们想要打印出数组中2号角标的元素只需要,
System.out.print(“arr[2]=”+arr[2]);
就可以了,如果我们想分别打印出数组中的其它元素,只需要把上面这段代码中的“2”角标换成其它编号的角标就可以了。但是这里有一个问题,分别获取数组中的元素这是一个重复性的操作。如果数组中的元素比较多,那么我们在获取元素是就会很麻烦。所以在获取数组元素是我们一般会用到遍历(循环)。
例一,
/*
定义功能,用于打印数组中的元素。元素间用逗号隔开。
*/
public static void main(String[] args) {
//定义一个数组
int[] arr={12,33,5,66,4};
Dayin(arr);
}
//数组的打印功能
public static voidDayin(int[] arr){
System.out.print("[");
for(intx=0;x<arr.length;x++){
if(x==arr.length-1)
System.out.println(arr[x]+"]");
else
System.out.print(arr[x]+",");
}
System.out.println("--------------------------------");
}
我们把“数组打印”的功能封装在了函数“Dayin”内,想要使用它时只需要调用它的函数名并给它赋值就可以了。
我们可以看到,“Dayin”函数内主要功能是由for循环语句实现的,这里我们需要注意一下“length”关键字,我们可以通过“数组名.length”的方式获取到数组的长度,从而可以更加方便的对不同长度的数组进行功能操作。
例二,
/*
定义功能,获取数组中的最大值。
*/
public static void main(String[] args) {
//定义一个数组
int[] arr={12,33,5,66,4};
int max=getMax(arr);
System.out.println("Max="+max);
int max1=getMax1(arr);
System.out.print("Max1="+max1);}
//方式一:
public static int getMax(int[] arr)
{
int max=0;
for(intx=1;x<arr.length;x++){
if(arr[x]>arr[max])
max=x;
}
returnarr[max];
}
//方式二:
public static intgetMax1(int[] arr)
{
intmax=arr[0];
for(intx=1;x<arr.length;x++){
if(arr[x]>max)
max=arr[x];
}
return max;
}
要得到数组中的最小值,需要先拿数组中的两个元素进行比较,得到一个较大的,然后再拿这个较大元素和第三个元素进行比较得到一个较大的,像这样逐一比较下去,最后得到的就是数组中的最大元素了。因此我们就要用到数组的遍历,而且要定义一个变量记录较大值。
在方法内定义一个变量“max”,初始化为0或数组的0角标位的元素然后让它与1角标位的元素进行比较,把较大的元素的值赋给max变量然后继续循环和后边的元素进行比较,最后“max”或者“arr[max]”就是数组内的最大元素。
注:从上边这两个例子可以看出,在对数组进行遍历是,我们一般都会用到for循环语句。
2、对数组中的元素进行排序
对数组元素的操作,除了求最值以外,“排序”也是一种经常用到的操作,排完序的数组使用起来更加的方便直观。
第一种,选择排序:
选定一个角标位逐一和其他角标位的元素进行比较,把需要的元素存到选定的角标位。
例,
public staticvoid Paixu(int[] arr)
{
inta=0;
for(intx=0;x<arr.length-1;x++)
{
for(inty=x+1;y<arr.length;y++)
{
if(arr[x]>arr[y]){
//换位操作
a=arr[x];
arr[x]=arr[y];
arr[y]=a; }
}
}
}
上面这个函数的功能就是,使给定数组内的元素按从小到大的顺序排列。功能的原理是,先选定0号角标位元素和1好角标位的元素进行比较,如果1号角标位的元素更小一些,就进行换位操作,使0号角标位保存较小的元素,就这样循环比较下去,等和最后一个角标位比较完成,那么0号角标位就是数组中最小的元素。接下来,选定1号角标位进行上边的循环,循环结束后1号角标位就是数组内第二小的元素。就这样各个角标位就按从小到大的顺序依次得到了它对应的元素。所以这个功能我们用到了for循环的嵌套,且内循环结束一次最小值出现在头角标位置上。
第二种,冒泡排序:
例,
public static void Maopao(int[] arr)
{
for(intx=0;x<arr.length-1;x++)
{
for(inty=0;y<arr.length-x-1;y++)
{
if(arr[y]>arr[y+1])
{
inta=arr[y];
arr[y]=arr[y+1];
arr[y+1]=arr[y];
}
}
}
}
冒泡排序的原理是让数组内相邻角标位的两个元素进行比较即0角标位和1角标位的元素,如果0角标位元素的值大于1角标位元素的值就进行换位操作,否则不换位。然后是1角标位的元素和2角标位的元素比较,依次这样比下去,当内循环结束一次时数组内的最大值就出现在了最后一个角标位上。而再次进入内循环时,依然从数组的0角标位开始,但是末角标位的值已经确定,不需要参加比较了,所以“y<arr.length-x-1”中的“-x”就是让内循环的次数随x的变化而变化,而“-1”则是因为如果不“-1”,当x=0时,内循环的arr[y+1]的角标位会随着循环次数和y值的增大而取到越位的角标,所以为了防止报角标越位错误必须“-1”.
注:1、上边的换位操作,首先定义一个第三变量a。然后把arr[x]的值给a,接着再把arr[y]的值给arr[x],再把a里边原本属于arr[x]的值给arr[y],这样就实现了arr[x]和arr[y]内的值的换位。
2、因为当比较到最后只剩下数组内的一个元素时就不需要再进行比较,所以控制循环次数的外循环的循环条件表达式要设定为“x<arr.length-1”。
3、在实际的开发当中,当我们要用到排序时会使用java中已经定义好的一种排序方式,即Arrays.sort(arr);。
3、查找
查找最简单的方式就是在对数组进行遍历时同时进行判断。
例,
public static int Chazhao(int[] arr,int key){
for(intx=0;x<arr.length;x++){
if(arr[x]==key)
returnx;
}
//当查找的对象不再数组内时返回-1
return -1;
}
还有一种效率更高的查找方式—折半查找,例:
public static int ZheBan(int[] arr,int key)
{
intmin,max,mid;
min = 0;
max =arr.length-1;
mid =(max+min)/2;
while(arr[mid]!=key)
{
if(key>arr[mid])
min= mid + 1;
elseif(key<arr[mid])
max= mid - 1;
if(min>max)
return-1;
mid= (max+min)/2;
}
return mid;
}
在用折半查找时,所提供的数组必须是有序的数组。而在这个前提下,折半查找的原理就是,取中间角标位的元素和要查找的元素做比较。如果两个元素相等,那么元素的位置就确定了。如果不相等,分为两个情况,如果该元素大于中间角标位的元素那么就改变取中间角标的区间为从原中间角标的“+1”角标位开始到末角标位结束,再取中间角标,使其元素与所求元素做比较。如果中间角标位的元素大于该元素,区间就变为从0角标位到原中间角标位“-1”的角标位,再取中间角标位。通过这样的反复比较,最后所取的中间角标的元素与所求元素相等时,那么该中间角标位就是所求元素的位置了。
*关于内存
局部变量(方法中的变量、方法中的参数变量和for循环定义的变量都是局部变量)都存放在栈中,当变量停止使用时,它的内存会自动释放。
堆里边存放的是实体(实体包括数组和对象),每一个实体都有一个存放位置,都是用来存放数据的且,存放的数据都有默认值。当堆中的实体没有被使用时,会通过垃圾回收机制进行内存的释放。