数组(array)用来存储一个元素个数固定且元素类型相同的有序集。
一旦数组被创建,它的大小是固定的。使用一个数组的引用变量,通过下标来访问数组中的元素。
一、一维数组
声明数组变量
elementType[] arrayRefVar; (元素类型[] 数组引用变量;)
或
elementType arrayRefVar[]; (元素类型 数组引用变量[];)
注:推荐使用elementType[] arrayRefVar;(元素类型[] 数组引用变量;)
风格。
其中elementType可以是任意数据类型,但是数组中所有元素都必须具有相同的数据类型。例如:
double[] myList;
上面代码声明的myList,它引用一个具有double型元素的数组。
创建数组
不同与基本数据类型变量,声明一个数组变量时并不在内存中给数组分配任何空间,只是创建一个对数组的引用的存储位置。如果变量不包含对数组的引用,那么这个变量的值为null。除非数组已经被创建,否则不能给它分配任何元素。
声明数组之后,可以使用new操作符创建数组,并将它的引用赋给一个变量。
arrayRefVar = new elementType[arraySize];
上述语句做了两件事:1)使用new elementType[arraySize]
创建了一个数组;2)把这个数组的引用赋值给变量arrayRefVar。
当然,也可以把声明数组变量、创建数组和将数组引用赋值给变量这三个步骤合在一条语句中。
elementType[] arrayRefVar = new elementType[arraySize];
(元素类型[] 数组引用变量 = new 元素类型[数组大小];)
或
elementType arrayRefVar[] = new elementType[arraySize];
(元素类型 数组引用变量[] = new 元素类型[数组大小];)
例如:
double[] myList = new double[10];
使用如下语句给元素赋值:
arrayRefVar[index] = value;
例如:
myList[0] = 7.7;
myList[1] = 8.8;
...
注:数组变量看起来似乎是存储了整个数组,但实际上它存储的是只想数组的引用。严格来讲,一个数组变量和一个数组是不同的,但多数情况下它们之间的差异是可以忽略不计的。
数组大小和默认值
给数组分配空间时,必须指定该数组能够存储的元素个数,从而确定数组的大小,且创建数组之后就不能修改其大小。可以使用arrayRefVar.length
得到数组的大小。
当数组创建后,它的元素被赋予默认值,数值型基本数据类型的默认值为0,char型默认值为’\u0000’,boolean型默认为false。
访问数组元素
数组元素是通过下标访问的,数组下标是基于0的,即其范围为从0开始arrayRefVar.length - 1
结束。
数组中每个元素都可以通过以下语法表示,称为下标变量。
arrayRefVar[index];(数组引用变量[下标];)
数组初始化
Java使用数组初始化语法,将数组的声明数组、创建数组和初始化数组结合到一条语句中:
elementType[] arrayRefVar = {value0, value1, value2, ..., valuek};(元素类型[] 数组引用变量 = {值0, 值1, 值2, ..., 值k};)
例如:
double[] myList = {1.0 ,2.0, 3.0, 4.0};
等价于
double myList = new double[4];
double[0] = 1.0;
double[1] = 2.0;
double[2] = 3.0;
double[3] = 4.0;
注:数组初始化语法语句中不使用new操作符。使用数组初始化语法时,必须将声明、创建和初始化都放在一个语句中。将它们分开会产生语法错误。例如以下语句是错误的:
// error
double[] myList;
myList = {1.0 ,2.0, 3.0, 4.0};
处理数组
处理数组元素的时候,经常使用for循环,理由如下:
1)数组中所有元素都是同一类型的,可以使用循环以同样的方式反复处理这些元素;
2)由于数组的大小时已知的,所以很自然地使用for循环。
Java支持一个简便的for循环,即foreach循环,即不使用下标变量就可以顺序的遍历整个数组。具体语法为:
for (elementType element : arrayRefVar) {
// do something
}
例如:
for (double e : myList) {
System.out.println(e);
}
即对数组myList中的每一个元素e进行相应操作。注意,变量e必须声明为与数组中元素相同的数据类型。
但是,当需要以其他顺序遍历数组或是改变数组中的元素时,还是必须用下标变量。
数组的复制
要将一个数组中的内容赋值到另一个数组中,需要将该数组中的每个元素复制到另外一个数组中。
当需要将要复制一个数组或数组一部分时,使用赋值语句(=),即list1 = list2;
是并不能将list1引用的数组内容复制给list2d,而只是将list1的引用复制给了list2。执行赋值语句后,list1和list2都会指向同一个数组。
在Java中,==可以使用赋值语句复制基本数据类型,但不能复制数组。==将数组变量赋值给另一个数组变量,实际是将一个数组的引用复制给另一个变量,使得两个变量都指向相同的内存地址。
复制数组的方法有三种:
1)使用循环语句逐个复制数组中的元素;
2)使用System类中的静态方法arraycopy;
3)使用clone方法复制数组。
使用循环复制:
int[] sourceArray = {1, 2, 3};
int[] targetArray = new int[sourceArray.length];
for (int i = 0; i < sourceArray.length; i++) {
targetArray[i] = sourceArray[i];
}
使用java.lang.System类中的arraycopy方法复制数组,语法如下:
System.arraycopy(sourceArray, srcPos, targetArray, tarPos, length)
srcPos和tarPos分别表示源数组sourceArray和目标数组targetArray中的起始位置,length指定复制到元素个数。
上述循环方法可以如此改写:
System.arraycopy(sourceArray, 0, targetArray, 0, sourceArray.length)
arraycopy方法没有给目标数组分配内存空间,因此复制前必须创建目标数组并为它分配内存空间。复制完成后两个数组内容相同,但占有独立的内存空间。
数组在方法中的使用
将数组传递给方法
当一个数组传递给方法时,实际是数组的引用被传递给方法。
int[] myList = {1, 2, 3};
public static void printArray(int array) {
for (int i = 0; i < myList.length; i++) {
System.out.println(array[i]);
}
}
// 调用
printArray(myList);
printArray(new int[]{1, 2, 3})
上述代码printArray(new int[]{1, 2, 3})
中没有显示的应用变量,这样的数组称为匿名数组。语法为:
new elementType[]{value0, value1, ..., valuek};
Java使用按值传递的方式将实参传递给方法。但传递基本数据类型变量和传递引用类型变量有很大的不同。
- 对于基本数据类型参数,传递的是实参的值;
- 对于数组类型参数,参数值是数组的引用,给方法传递的是这个引用。从语义上讲,就是参数传递时信息共享,即方法中的数组和传递的数组是一样的,所以如果方法中的数组改变了,方法外的数组相应的也变化了。例如:
public class Test1 {
public static void main(String[] args) {
int x = 1;
int[] y = new int[3];
m(x, y);
System.out.println("x is:" + x);
System.out.println("y[0] is:" + y[0]);
}
private static void m(int num, int[] array) {
num = 666;
array[0] = 777;
}
}
/* output
x is:1
y[0] is:777
*/
可以看到x调用m方法后还是1,但是y[0]却变成了777,因为y和array尽管是两个独立的变量,但是它们指向的是同一个数组,当调用m方法后x和y的值分别传递给了num和array,因为y是数组的引用值,所以array也是具有相同的引用值。
注意:数组在Java中是对象,JVM将对象存储在一个称为堆的内存区域中,堆用于动态内存分配。
从方法中返回数组
当一个方法中返回一个数组时,数组的引用被返回。
可变长参数列表
具有相同类型的可变长度的参数可以传递给方法,并作为数组对待。声明如下:
typeName... parameterName (类型名... 参数名)
在方法声明中,指定类型后紧跟省略号(…)。只能给方法中指定一个可变长参数,同时该参数必须时最后一个参数,任何常规参数必须在它之前。
Java将可变长参数当成数组对待,可以将一个数组或是数目可变的参数传递给可变长参数。当数目可变的参数调用方法时,Java会创建一个数组并把参数传给他。
public class Test2 {
public static void main(String[] args) {
printArray(1, 2, 3);
printArray(new int[]{4, 5, 6});
}
private static void printArray(int... numbers) {
for (int e : numbers) {
System.out.print(e + " ");
}
System.out.println();
}
}
/* output
1 2 3
4 5 6
*/
数组的查找
如果一个数组排好序了,对于查找数组中的一个元素,二分查找比线性查找更加高效。
Arrays类
java.util.Arrays类中包含一些实用的方法用于常见的数组操作,比如排序和查找。
排序
可以使用sort或者parallelSort对整个或部分数组排序。如果计算机有多个处理器,那么parallelSort将更加高效。
int[] numbers = {3, 1, 2};
java.util.Arrays.sort(numbers);
java.util.Arrays.parallelSort(numbers);
char chars = {'a', 'A', '4', 'F', 'D', 'P'};
java.util.Arrays.sort(numbers, 1, 3);
java.util.Arrays.parallelSort(numbers, 1, 3);
调用sort(numbers)对整个数组排序,调用sort(numbers, 1, 3)可对chars[0] 到 chars[3 - 1]的部分排序。
查找
二分查找法在数组中查找对应关键字,数组必须提前按升序排列。如果数组中不存在关键字,方法返回-(插入点下标 + 1),个人理解“插入点下标”即为如果该关键字存在那么它应该在数组中所处位置:
int[] list = {1, 2, 3, 4, 5, 6, 7};
System.out.println(java.util.Arrays.binarySearch(list, 7)); // 6
System.out.println(java.util.Arrays.binarySearch(list, 8)); // -8
其中,8如果在list中存在,那他按顺序应该在index为7的位置,因此返回-(7 + 1),即-8。
两个数组是否相等
可以采用equals检测两个数组是否相等。
int[] list1 = {1, 2};
int[] list2 = {1, 2};
int[] list3 = {1, 3};
java.util.Arrays.equals(list1 , list2); // true
java.util.Arrays.equals(list1 , list3); // false
填充数组
使用fill方法填充整个或部分数组。
int[] list1 = {1, 2};
int[] list2 = {1, 2, 3};
java.util.Arrays.fill(list1 , 7); // 用7填充整个数组 [7, 7]
java.util.Arrays.fill(list2 , 1, 2, 8); // 用8填充部分数组,下标含前不含后 [1, 8, 3]
数组转换为字符串
使用toString犯法将数组返回为字符串。
int[] list = {1, 2};
System.out.print(java.util.Arrays.toString(list)); // [1, 2]
命令行参数
main方法可以从命令行接收字符串参数或从文件中获取参数。
java Test arg0 arg1 ... argk
或者
java Test < testParas.txt
arg0 arg1 … argk都是字符串,但是在命令行中,不需要放在双引号内,这些字符串是用空格分隔,如果字符串包含空格,那就必须使用双引号括住。
当调用main方法时,Java解释器会创建一个数组存储命令行参数,然后将数组引用传递给args,如果具有n个参数,java解释器会创建一个如下数组:
args = new String[n];
注:如果运行时灭有传递字符串参数,那么使用new String[0];
创建数组,这种情况下,该数组是长度为0的空数组。args是对整个空数组的引用,因此,args不是null,但是args.length为0.
二、多维数组
二维数组
在表格或是矩阵中的数据可以表二维数组。二维数组中的元素通过行和列的下标来访问。
二维数组的本质也是个数组,只是它的每个元素都是一个数组。因此大多数原理、方法与一维数组一致。
二维数组的声明和创建
声明:
数据类型[][] 数组名;
或者
数据类型 数组名[][]; // 不推荐
创建:
matrix = new int[5][5];
注:使用new操作符创建数组时,第一个下标必须指定。语法new int[][]
是错误的。
数组长度:
二维数组也通过.length
的方式获取数组长度。
数组的处理:
二维数组一般用嵌套for循环来处理。
锯齿数组
锯齿数组就是二维数组中的每一行的长度各不相同。
多维数组
多维数组与二维数组大致一致。