3.11 数组和枚举
3.11.1 数组
(1)数组的声明
数组是一种数据结构,用来存储同一类型值的集合。
在声明数组变量时,需要指出数组类型(也就是数组中数据元素的类型)和数组变量的名字,有两种声明数组的方式:
int[] a; 或
int a[];
注:大多数程序员喜欢第一种风格,因为它将类型int[](整型数组)与变量名a区分开来。 |
以上语句只是声明了变量a是一个数组,并没有将a初始化为一个真正的数组(即没有分配内存空间给它),应该使用new运算符来创建数组,如:
int[] a =new int[100];//创建一个可以存储100个整数的数组
数组可以通过下标值访问其中任何一个元素,数组的下标从0开始。一旦创建了数组,就可以给数组元素赋值。在访问数组时不允许越界访问,即如果创建了100个元素的数据而试图访问a[100](任何在0~99之外的下标),程序就会引发“array index out of bounds”异常而终止。
可以通过for each循环方便地遍历数组,如:
/*声明并初始化一个有100个元素的整型数组*/ int[] a = new int[100]; /*数组赋值*/ for (int i=0;i<a.length ;i++ ) //可以使用array.length获得数组中元素的个数 a[i]=i+1; /*用for each遍历访问数组*/ for (int element:a ) System.out.println(element); /*用常规for循环遍历访问数组*/ for (int i=0;i<a.length ;i++ ) System.out.println(a[i]); |
注意:一旦创建了数组,就不能再改变它的大小(可以改变每个数组元素)。如果经常需要再运行过程中扩展数组的大小,就应该使用另一种数据结构—数组列表(array list)。
(2)数组初始化以及匿名数组
在Java中,提供了一种创建数组对象并同时赋予初始值的简化书写形式,如:
int[] smallInt = {2,3,5,6,7,8,9}; //不需要调用new
而new int[] { 2,3,5,6,7,8,9 }可初始化一个匿名数组,数组的大小就是初始值的个数,用这种语法形式可在不创建新变量的情况下重新初始化一个数组。如
int[] smallInt=new int[100]; System.out.println("Array Size:"+smallInt.length); smallInt=new int[]{1,2,3,4,5,6,7,8}; //用匿名数组重新初始化smallInt System.out.println("Array Size:"+smallInt.length); // smallInt=new int[100]{1,2,3,4,5,6,7,8}; ->注意是语法错误 输出结果: Array Size:100 Array Size:8 |
注:在Java中,允许数组长度为0,在编写一个返回结果为数组的方法时如果碰巧结果为空,则这种语法形式就有用了,可创建一个长度为0的数组:new eleType[0];注意:数组长度为0与null不同。
(3)数组拷贝
先看以下代码:
int[] luckyNumbers = smallPrimes;
luckyNumbers[5]=12; //smallprimes[5] is also 12
拷贝数组变量
这里将一个数组变量拷贝给另一个数组变量,如上图所示仅是变量引用的拷贝,即将两个变量引用到同一个数组空间。
注意:
如果希望将一个数组的所有值拷贝到一个新的数组中(另一块内存区域),就要用到java.util.Arrays类的copyOf方法:
int[] copiednumbers = Arrays.copyOf (luckynumbers,luckynumbers.length);
其中第2个参数是新数组的长度,这个方法通常用来增加数组的大小,如下例:
lukyNumbers = Arrays.copyOf (luckNumbers,2*luckyNumbers.length);
如果数组元素是数值型,那么多余元素将被赋值为0;如果数组元素是布尔型,则多余元素被赋值为false。相反如果长度小于原始数组的长度,则只拷贝原始数组最前面的数据元素。
int[] old1={1,2,3,4}; int[] old2={11,22,33,44,55}; old2=Arrays.copyOf(old1,old2.length); //需要在程序顶部import java.util.Arrays; for(int ele:old2) System.out.printf("%d ",ele); 输出:1 2 3 4 0 |
注释:在Java SE6之前,用System类的arraycopy方法将一个数组的元素拷贝到另一个数组中。调用这个方法的语法格式为:
System.arraycopy(from,fromIndex,to,toIndex,count);
数组to必须有足够的空间存放拷贝的元素。
int[] arr1={1,2,3,4,5,6}; int[] arr2={1001,1002,1003,1004,1005,1006,1007,1008}; System.arraycopy(arr1,2,arr2,2,4); for(int ele:arr2) System.out.printf("%d ",ele); System.out.println(); 输出:1001 1002 3 4 5 6 1007 1008
|
注意:Java数组与C++数组在堆栈上有很大不同,但基本上与分配在堆(heap)上的数组指针一样,也就是说:
int[] a = new int[100]; //java
不同于
int a[100]; //C++
而等同于
int* a = new int[100];//C++ 数组指针
java中的[]运算符被预定义为检查数组边界,而且没有指针运算,即不能通过a+1得到数组的下一个元素。
(4)命令行参数
每个Java应用程序都有一个带String[] arg参数的main方法,该方法将可以接收一个字符串数组作为命令行参数:
public static void main(String[] args)
args[0] ->第一个参数(程序名不存储在args数组中)
args[1] ->第二个参数
…
args.length ->参数个数
例如:执行“java Message –g jack world”,则
args[0]:“-g”
args[1]:“jack”
args[2]:“world”
(5)数组排序
要想对数值型数组进行排序,可以使用java.util.Arrays类中的sort方法:
int[] a = new int[10000];
…
Arrays.sort(a);
这个方法使用了优化的快速排序算法。
程序示例:
用数组产生一个抽彩游戏中的随机数值组合,假如抽彩是从49个数值中随机抽取6个,那么程序可能的输出结果为:
Bet the following combination.It’s make you rich!
4,7,8, 21,22,34
首先设置两个数组:numbers存放数值1,2,3,…,n;另一数组result存放抽取出来的数值;
然后开始抽取k个数值,Math.random方法(java.lang.Math类)将返回一个0到1之间(包含0但不包含1)的随机浮点数,用n乘以这个浮点数,再经过强制类型转换就可以得到从0到n-1之间的一个随机整数。抽取数值时通过抽取数组下标r来进行。
现在必须确保不会再次抽取到那个数值,因此这里用数组中的最后一个数值改写numbers[r],并将n-1。在抽取了k个数值之后,就可以对result数组进行排序。
import java.util.Arrays; //java.lang.Math默认导入 public class SecondSample { public static void main(String[] args) { int n=49; int k=6; //随机抽取6个
//初始化numbers和result数组 int[] numbers=new int[n]; int[] result=new int[k];
//对numbers数组进行赋值并显示 for (int i=0;i<numbers.length ;i++ ) numbers[i]=i+1; System.out.println("numbers:"); for (int ele:numbers ) System.out.printf("%d ",ele); System.out.println(); //随机生成不重复的下标r,将抽取元素保存到result数组 int r=0; for (int i=0;i<result.length ;i++ ) { r=(int)(Math.random()*n); //随机抽取下标 result[i]=numbers[r]; numbers[r]=numbers[n-1]; n-=1; //修正抽取下标范围 } //对抽取元素进行排序并显示 Arrays.sort(result); //对抽取数组排序 System.out.println("Bet the following combination.It’s make you rich!"); for (int ele:result ) System.out.printf("%d ",ele); System.out.println(); }//end main() } |
java.util.Arrays常用API
static String toString(type[] a) ——jdk5.0 返回包含a中数据元素的字符串,这些数据元素被放在括号内,并用逗号分隔; 参数a类型为int、long、short、char、byte、boolean、float或double数组 |
static type copyOf(type[] a,int length) static type copyOf(type[] a,int start,int end) ——jdk6.0 返回与a类型相同的一个数组,其长度为length或者end-start,数组元素为a的值 type:int、long、short、char、boolean、float等 start:起始下标(包含这个值) end: 终止下标(不包含这个值),这个值可能大于a.length,这种情况下结果为0或false length:拷贝的数据元素长度,如果length大于a.length结果为0或false;否则数组中只有前面length个数据元素的拷贝值; |
static void sort(type[] a) 采用优化的快速排序算法对数组进行排序 |
static int binarySearch(type[] a,type v) ——jdk6.0 static int binarySearch(type[] a,int start,int end,type v) 采用二分搜索算法查找值v,如果查找成功,则返回相应的下标值;否则,返回一个负数值r。(-r)-1是为保持a有序v应插入的位置。 数组a:注意数组应是有序数组(二分法的前提),可先用Arrays.sort()排序; start:起始下标(包含这个值) end: 终止下标(不包含这个值) v:同a的数据元素类型相同的值,待查值 |
static void fill (type[] a,type v) 将数组的所有数据元素值设置为v |
static boolean equals(type[] a,type[] b) 如果两个数组大小相同,并且下标相同的元素都对应相等,返回true,否则返回false |
此外,在java.lang.System类中还提供了一个arraycopy方法
static void arraycopy(Object from,int fromIndex,Object to,int toIndex,int count)—jdk1.1 将第一个数组from中的元素拷贝到第二个数组to中 from:任意类型数组 formIndex:原始数组from中待拷贝元素的起始下标 to:与from同类型的数组 toIndex:目标数组to放置拷贝元素的起始下标 count:拷贝的元素数量 |
(6) 多维数组
多维数组使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式。
可以使用二维数组(也称矩阵)存储这些信息,在java中声明一个二维数组如下:
double[][] balances;
与一维数组一样,在调用new对多维数组进行初始化之前不能使用它,初始化如下:
balances=new double[lines][cols];
如果知道数组元素,可以不调用new,直接用简化的方式对数组进行初始化,如:
int[][] magicSquare= { {2,3,4,5}, {5,10,12,34}, {5,6,7,8}, {5,43,3,2} }; |
注:for each循环不能自动处理二维数组的每一个元素,它是按照行(也就是一维数组)处理的,如果要访问二维数组a的所有元素,需要使用循环嵌套,如:
for(double[] row:a)
for(double value:row)
do something with value
要想快速打印一个二维数组的元素列表,可以调用:
System.out.println(Arrays.deepToString(a));
输出格式为:
[{1,2,3,4,},{11,22,33,44},{1,2,3,4,},{1,2,3,5,}]
(7)不规则数组
Java在数组方面的优势:Java实际上没有多维数组,只有一维数组。多维数组被解释为“数组的数组”。
例如二维数组的balances[i]引用第i个子数组,也就是二维表的第i行,它本身就是一个数组,balance[i][j]就是引用这个数组的第j项。
由于可以单独地存取数组的某一行,所以可以让两行交换。如:
double[] temp=balances[i];
balances[i] = balances[i+1];
balances[i+1] = temp;
同时这个特性还可方便地构造一个“不规则”数组,即数组每一行可以有不同的长度。
要想创建一个不规则的数组,首先需要分配一个具有所含行数的数组:
int[][] odds = new int[NMAX+1][];
接下来,分配这些行:
for(int n=0;n<=NMAX;n++)
odds[n] = new int [n+1];
在分配了数组之后,如果没有超出边界,就可以采用通常的方式遍历元素了,如:
for(int i=0;i< odds.length;i++)
for (int j=0;i< odds[i].length;j++)
{
odds[i][j]=…;
}
注意:
在Java中的声明-
double[][] balances = new double [10][6]; //java
不同于
double balances[10][6];//c++
也不同于
double (*balances)[6]=new double[10][6]; //c++
而是创建了一个包含10个指针的一个数组,如同:
double** balances = new double* [10]; //c++
然后,指针数组的每一个元素被分配了一个包含6个数值的数组:
balances[i]= new double[6]; //i=0,1,…,9
当创建new double[10][6]时这个循环将自动执行。当需要不规则数组时,只能单独地创建行数组。
3.11.2 枚举类型
有时候变量的取值只在一个有限的集合内,超过这个集合的值即为错误值。例如销售的衣服只有红、绿、蓝三种颜色,如果变量保存了黑色即是一个错误值。
从JDK5.0开始,可以自定义枚举类型,枚举类型包括有限个命名的值,例如:
enum Color { RED,GREEN,BLUE};
这样,就可以声明这样一种类型的变量:
Color acolor=Color.GREEN;
Color类型的变量只能存储这个类型声明中给定的某个枚举值,或者null值,null表示这个变量没有设置任何值。
数组小结:
(一) 数组声明和初始化
声明:
int[] a;
初始化:
int[] a =new int[100];
或 int[] smallInt = {2,3,5,6,7,8,9};
通过匿名数组重新初始化:
smallInt=new int[]{1,2,3,4,5,6,7,8};
(二) 数组拷贝
变量引用关系拷贝:
int[] luckyNumbers = smallPrimes;
luckyNumbers[5]=12; //smallprimes[5] is also 12
数组元素值的拷贝:
int[] copiednumbers = Arrays.copyOf (luckynumbers,luckynumbers.length);
或 System.arraycopy(from,fromIndex,to,toIndex,count);
(三)命令行参数
public static void main(String[] args)
args[0] ->第一个参数(程序名不存储在args数组中)
args[1] ->第二个参数
…
args.length ->参数个数
(四)数组排序
要想对数值型数组进行排序,可以使用java.util.Arrays类中的sort方法:
int[] a = new int[10000];
…
Arrays.sort(a);
这个方法使用了优化的快速排序算法。
注:Math.random方法(java.lang.Math类)将返回一个0到1之间(包含0但不包含1)的随机浮点数,用n乘以这个浮点数,再经过强制类型转换就可以得到从0到n-1之间的一个随机整数。
(五)多维数组和不规则数组
多维数组声明和初始化:
double[][] balances;
balances=new double[lines][cols];或
int[][] magicSquare=
{
{2,3,4,5},
{5,10,12,34},
{5,6,7,8},
{5,43,3,2}
};
不规则数组:
首先需要分配一个具有所含行数的数组:
int[][] odds = new int[NMAX+1][];
接下来,分配这些行:
for(int n=0;n<=NMAX;n++)
odds[n] = new int [n+1];
Java实际上没有多维数组,只有一维数组。多维数组被解释为“数组的数组”。
例如二维数组的balances[i]引用第i个子数组,也就是二维表的第i行,它本身就是一个数组,balance[i][j]就是引用这个数组的第j项。
数组最常用相关类之一:java.util.Arrays类
枚举类型小结:
自定义枚举类型:enum Color { RED,GREEN,BLUE};
定义枚举变量: Color acolor=Color.GREEN;
Color类型的变量只能存储这个类型声明中给定的某个枚举值,或者null值,null表示这个变量没有设置任何值。