数组指的是将一组相同类型的变量放到一个集合,这个集合就是数组。
一.数组的声明:
type[] arrayName;
type arrayName[];
这两种方式中,更推荐第一种,因为第一种声明方法更加符合Java中定义变量时的“类型 名称”格式。
例:int[] array;这里是声明了一个名为array的数组,array里边装的变量都是int类型
注:数组是一个引用类型,在定义引用类型的变量时只是一个变量,这个变量没有指向任何有效的内存空间,所以我们不能在声明时给定数组的大小,我们只有在初始化时才能够给定数组大小!
例: int[10] array; 这样声明数组ok吗? 不可以!
二、数组的初始化:
type[] arrayName = {1, 2 ,3 ,4}; 数组初始化简写方式,不能分开写
type[] arrayName = new type[10];//10表示当前给数组开辟的内存空间大小
type[] arrayName = new type[]{1, 2, 3, 4};//这个[]中不能给定数组大小
第一种是数组的静态初始化,后两种是数组的动态初始化方法。
例:type[] arrayName = new type[4]{1, 2, 3, 4};这种声明方法是对是错?
答案:错误。
原因:type[] arrayName = new type[]{1, 2, 3, 4};这种数组的声明方式声明的数组,它的大小石油后边的{1, 2, 3, 4}这个类的样子来决定的,这时如果在[]中填入数组的长度会导致两种声明方法之间相互冲突。
例2:
int[] brray;
brray = {1, 2, 3};
注意:数组初始化不能分开去写!数组是引用类型,所以数组使用之前必须要开辟内存空间没有开辟内存空间就会出现一个空指针异常。
三.数组的下标范围:
int[] array=new int[10];
这句话的意思是定义一个长度为10的int数组,那么当我们要去访问第十个元素时,如果使用array[10],那么编译器会报错,报错的原因是:
数组的序号是从0开始标记的,那么也就是说,实际上在数组中,每一个单元对应的下标都是其序号减一,所以说array[9],才是第十个元素,另外如果使用数组下标时超出了数组的合法范围,那么编译器会报一个越界的错误,而array[10]的意思是访问数组的第11个元素,这对于只有十个元素大小的数组来说就会引起越界错误。
四、数组的操作:
int[] array=new int[10];
1.数组单元可以通过索引去访问。
例:array[9]=10;这句话的意思是把10付给array[9]这个单元。
2.刚刚定义好的数组中所有元素都是该类型的0值,普通类型就是0,引用类型就是null。
3.数组本身是一种有序的集合,我们通常多用循环方式来对其进行操作。
4.数组有一个可以使用的length方法,这个length方法可以用来求数组的长度。
例:int length=array.length();
这句话的意思是把数组array的长度赋值给变量length。
5.遍历数组的语句:foreach
语句格式:
for(元素类型 元素变量:遍历的集合对象){ }
6.数组输出
System.out.println(Arrays.toString(arr));
这个代码的意思是把数组arr每个单元加起来,视做字符串输出,可以完整的输出如“{1,2,3,4}”这样的数组。
例:for(int a:array){ }就可以把数组array遍历一次。
五、数组的拷贝:
1.深拷贝与浅拷贝:
深拷贝:将一个对象从内存中完整拷贝给另外一个对象,从内存中开辟新区域来储存对象。
浅拷贝:没有从内存中拷贝,仅仅是对引用进行一次拷贝。
深拷贝与浅拷贝的主要区别在于是否真正从内存中申请了一个新的空间来储存复制
例:int[] list1={1,2,3,4};
int[] list2=list1;
这之后如果让list1的第一项变成100,那么list2[0]是多少?
还是100,因为这两个数组赋值语句赋的都是引用类型,实际上这两个引用类型指向同一个数组。这就是一个浅拷贝,数组本身还是一个,被拷贝的只是数组的引用。
常用的四种深拷贝:
(1)for循环拷贝:
int[] arr = {1, 2, 3, 4, 5, 6}; int[] brr = new int[arr.length]; for(int i=0; i<arr.length; i++){ brr[i] = arr[i]; }
(2)clone方法拷贝:
clone方法是从Object类继承过来的,基本数据类型(int ,boolean,char,byte,short,float ,double,long)都可以直接使用clone方法进行克隆,由于String类的不变性,String类也可以用此方法来拷贝。**
clone方法是深拷贝,是创建了一个新的空间这个空间的大小和内容都是和被拷贝的元素是相同的。
int[] arr = {1, 2, 3, 4};
int[] brr = new int[arr.length];
brr = arr.clone();
(3)使用Arrays.copyOf(T[] original, int newLength):
T[] original是原始数组 int newLength是新的数组长度。
int[] arr = {1, 3, 5, 7, 9};
int[] brr = Arrays.copyOf(arr,2*arr.length);
得到的新数组长度是原数组的两倍,所以新数组的后半部分全部由零取代。
如果新数组长度小于原数组,那么能拷贝多少就拷贝多少。
(4)使用System.arrayCopy(src, srcPos, dest, destPos, length)
这个方法的意思是,从索引为srcPos的位置开始复制src数组的单元,然后复制到dest数组的索引为destPos位置开始,复制length个单元。src和dest可以是同一个数组。
int[] arr1 ={1,2,3,4,5};
System.arrayCopy(arr1, 3, arr1, 2, 2);
这段代码的意思是:
将arr1从数字4开始 拷贝到arr1的数字3的位置, 拷贝2个数, 也就是说将4和5 拷贝到数字3的位置,相当于删除数字3。这就是一个典型的源数组和目标数组是同一个数组的例子。
六.二维数组:
1.声明:
int[][] array;
两个“[]”代表二维数组。
2.初始化:
首先二维数组的含义是数组的数组,这个是很重要的一点,在二维数组的初始化中体现的很明显。“数组的数组”这句话的意思是,二维数组可以看做是一个里边每个元素都是指向一个一维数组的引用的一个由引用组成的特殊的一维数组。
1 int[][] array = {{1,2,3}, {2,3,5}};
2 int[][] brray = new int[3][];
brray[0] = new int[3];
brray[1] = new int[3];
brray[2] = new int[3];
二维数组定义的一个常见错误。
注意:二维数组不可以这样定义“int[][] brray = new int[][3];”因为二维数组的含义是数组的数组,“int[][] brray = new int[3][];”的意思是,这是一个由三个int[]组成的二维数组。如果写错的话会变成一个不知道单元个数的错误数组!
3 int[][] crray = {new int[3], new int[3], new int[3]};
二维数组的拷贝:
二维数组的拷贝是一个很容易出现错误的一个点,其原因在于,二维数组本质上是一个装有指向一维数组的引用的数组,而如果像一维数组那样贸然使用深拷贝的方法之后,实际上只会创造一个装有指向和原二维数组一样的引用构成的一维数组,实际上数组还是只有一个。被复制的只不过是一个由引用组成的数组。正确的拷贝方式是要把数组的元素即每一个一维数组单独拿出来拷贝,把每一个元素对应的一位数组都进行一次拷贝,才能成功的拷贝数组。
例.用Arrays.copyOf拷贝二维数组:brray = Arrays.copyOf(array, array.length*2);
这就是一个很明显的错误拷贝,brray实际只是个和array一样装有引用的一维数组,而array里边的作为元素的一维数组实际上并没有被拷贝。
正确拷贝方式:
for(int i=0; i<array.length; i++){ brray[i] = Arrays.copyOf(array[i], array[i].length); }
从这个例子中我们可以得知,二维数组的拷贝,其实本质上就是用循环去对作为二维数组的元素的一维数组们进行逐个拷贝。
(1)clone方式拷贝二维数组:
for(int i=0; i<array.length; i++){
brray[i] = array[i].clone();
}
(2)System.arrayCopy()拷贝二维数组:
for(int i=0; i<array.length; i++){
System.arraycopy(array[i], 0, brray[i], 0, array[i].length);
}
(3)for循环拷贝数组
int[][] array = new int[][]{{10, 20, 40}, {25, 12, 50}, {18, 29, 46}};
int[][] brray = new int[3][3];
for(int i=0; i<array.length; i++){
for(int j=0; j<array[i].length; j++){
brray[i][j] = array[i][j];
}
}
(4)Arrays.copyOf()拷贝
for(int i=0; i<array.length; i++){
brray[i] = Arrays.copyOf(array[i], array[i].length);
}
注意:二维数组的组成元素完全可以是长度不同的一维数组,因为只要这些数组是元素类型相同的,那么它们就可以成为一个二维数组里边的元素。
例:int[][] array=new{{1,2},{3,4,5,6},{7,8}};
这样的声明是完全正确的。
数组的不同表示形式作为方法参数的不同结果:
public static void swap(int a, int b){
int tmp = a;
a = b;
b = tmp;
}
public static void swapByArray(int[] array){
int tmp = array[0];
array[0] = array[1];
array[1] = tmp;
}
上边是两个数据交换的函数,根据方法的相关知识,我们可以知道方法对于引用类型的形式参数的操作是会影响到实际参数的,但是对于基本数据类型的形式参数的操作是不会影响实际参数的。
int[] arr = {1, 2};
swap(arr[0], arr[1]);
System.out.println("swap: " +Arrays.toString(arr));
swapByArray(arr);
System.out.println("swapByArray: "+Arrays.toString(arr));
所以我们从上边的这段代码中这进行了两次调用方法,但是结果是不同的,原因就是虽然“arr”是引用类型,但是“arr[0],arr[1]”就是具体到数组的单元了,不是引用类型而是基本数据类型。而方法又会因为形式参数是否是引用类型而产生不同的结果。