若想要存放一连串相同数据类型的数据,使用数组是个相当好用的选择。此外,如果某个程序片段经常反复出现,那么将它定义成一个方法可以有效地简化程序代码。本章主要是介绍数组的基本用法与方法的应用,学完本章,将会对数组与方法的使用有更深一层的认识。
数组是由一组相同类型的变量所组成的数据类型,它属于引用类型。它们以一个共同的名称表示,数组中的每个元素则以下标来访问。数组依照存放元素的复杂程度分为一维数组、二维和多维数组,先来看看一维数组。
3.1 一维数组
一维数组可以存放上千万个数据,并且这些数据的类型是完全相同的。可以将它简单的理解为是相同数据类型的数据的集合。
3.1.1 一维数组的声明与内存的分配
要使用Java的数组,必须经过两个步骤:
(1)声明数组
(2)给数组分配内存
这两个步骤的语法如下:
数据类型 数组名[ ];或 数据类型[ ] 数组名;// 声明一维数组
数组名 = new 数据类型[个数];// 给数组分配内存
数组的声明格式里,“数据类型”是声明数组每个元素的数据类型,常见的类型有整型、浮点型与字符型等,也可以是引用类型。“数组名”是用来统一这组相同数据类型的元素的名称,其命名规则和变量的相同,建议使用有意义的名称为数组命名。数组声明后,接下来便是要配置数组所需的内存,其中“个数”是告诉编译器,所声明的数组要存放多少个元素,而“new”则是命令编译器根据括号里的个数,在内存中开辟一块内存供该数组使用。
需要注意的是,在Java中声明数组的时候,不能在[]中指定其长度。比如:
int score[3];// 错误
下面是关于一维数组的声明并分配内存给该数组的一个范例:
int score[]; // 声明整型数组score
score = new int[3];// 为整型数组score分配内存空间,其元素个数为3
在上例中的第一行,当声明一个整型数组score时,score可视为数组类型的变量,此时这个变量并没有包含任何内容,编译器仅会分配一块内存给它,用来保存指向数组实体的地址,如图3-1所示。
图3-1 声明整型数组
声明之后,接着要做内存分配的操作,也就是上例中第二行语句。这一行会开辟3个可供保存整数的内存空间,并把此内存空间的参考地址赋给score变量。其内存分配的流程如图3-2所示。
图3-2 内存分配
上图中的内存参考地址0x1000是假赋值,此值会因环境的不同而异。数组是属于非基本数据类型,因此数组变量score所保存的并非是数组的元素值,而是数组元素的首地址。
除了可以用两行来声明并分配内存给数组之外,也可以用较为简洁的方式,把两行缩成一行来编写,其格式如下:
数据类型 数组名[] = new数据类型[个数];
或
数据类型[] 数组名= new数据类型[个数];
上述的格式会在声明的同时,即分配一块内存空间,供该数组使用。下面的范例是声明整型数组score,并开辟可以保存10个整数的内存给score变量。
int score[] = new int[10];或int[] score = new int[10];
//声明一个元素个数为10的整型数组score,同时开辟一块内存空间供其使用
在Java中,由于整数数据类型所占用的空间为4个bytes,而整型数组score可保存的元素有10个,所以上例中占用的内存共有4 * 10 = 40个字节。图3-3是将数组score用图形来表示,可以比较容易理解数组的保存方式。
图3-3 数组的保存方式
3.1.2 数组中元素的表示方法
想要使用数组里的元素,可以利用索引下标来完成。格式如下:
数组名[index];
Java的数组索引编号由0开始,长度为n的数组的合法下标取指范围为:0~(n-1)。以上一节中的score数组为例,score[0]代表第1个元素,score[1]代表第2个元素,score[9]为数组中第10个元素(也就是最后一个元素)。图3-4为score数组中元素的表示法及排列方式:
图3-4 数组中元素的排列
注意:数组下标不要越界。
接下来,看一个范例。下面的程序里,声明了一个一维数组,其长度为3,利用for循环输出数组的内容后,再输出数组的元素个数。
范例:TestJava3_1.java
//下面这段程序说明了一维数组的使用方法
public class TestJava3_1 { public static void main(String args[]) { int i; int a[]; // 声明一个整型数组a a=new int[3]; // 开辟内存空间供整型数组a使用,其元素个数为3 for(i = 0; i < 3; i++) // 输出数组的内容 System.out.print("a["+i+"] = "+a[i]+", "); System.out.println(" 数组长度是:"+a.length); // 输出数组长度 } }
输出结果:
a[0] = 0, a[1] = 0, a[2] = 0,
数组长度是:3
程序说明:
1、第7行声明整型数组a;第8行开辟了一块内存空间,以供整型数组a使用,其元素个数为3。
2、第9~10行,利用for循环输出数组的内容。由于程序中并未给予数组元素赋值,因此输出的结果都是0。
3、第11行输出数组的长度。此例中数组的长度是3,即代表数组元素的个数有3个。要特别注意的是,在Java中取得数组的长度(也就是数组元素的个数)可以利用“.length”完成,如下面的格式:
数组名.length
也就是说,若是要取得TestJava3_1中所声明的数组a的元素个数,只要在数组a的名称后面加上“.length”即可,如下面的程序片段:
a.length; //取得数组a的长度
3.1.3 数组的静态初始化
如果想直接在声明时就给数组赋初值,可以利用大括号完成。只要在数组的声明格式后面再加上初值的赋值即可,如下面的格式:
数据类型 数组名[] = new 数据类型[]{初值0, 初值1, …, 初值n};
或简写成:
数据类型 数组名[] = {初值0, 初值1, …, 初值n};
在大括号内的初值会依序指定给数组的第1、…、n+1个元素。此外,在声明的时候,不需要将数组元素的个数列出,编译器根据所给出的初值个数来判断数组的长度。如下面的数组声明及赋初值范例:
int data[] = new int[]{32, 23, 45, 22, 13, 45, 78, 96, 43, 32}; // 数组声明并赋初值
或:
int data[] = {32, 23, 45, 22, 13, 45, 78, 96, 43, 32}; // 数组声明并赋初值
在上面的语句中,声明了一个整型数组data,虽然没有特别指明数组的长度,但是由于大括号里的初值有10个,编译器会分别依序指定给各元素存放,data[0]为32,data[1]为23,…,data[9]为32。
范例:TestJava3_2.java
//一维数组的赋值,这里采用静态初始化方式赋值 public class TestJava3_2 { public static void main(String args[]) { int i = 0; int a[] = {5, 6, 8}; // 声明一个整数数组a并赋初值 for(i=0; i<a.length; i++) // 输出数组的内容 System.out.print("a["+i+"]="+a[i]+", "); System.out.println(" 数组长度是:"+a.length); } }
输出结果:
a[0]=5, a[1]=6, a[2]=8,
数组长度是:3
除了在声明时就赋初值之外,也可以在程序中为某个特定的数组元素赋值。可以将程序TestJava3_2的第7行更改成下面的程序片段:
int a [] = new int[3];
a[0] = 5;
a[1] = 6;
a[2] = 8;
通常数组的遍历都需要用到循环,从数组下标0开始一直到a.length为止,输出数组的每个元素。除此之外,数组的遍历还可以利用一种特殊的手段来实现,将TestJava3_2.java改造之后如下:
int a[] = new int[]{5, 6, 8};
for(int iVal : a)//for_each结构,此时iVal表示具体的数组元素
System.out.print(iVal + " ");
System.out.println(" 数组长度是:" + a.length);
3.1.4 简单的范例:找出数组元素中的最大值与最小值
由前几节的范例可知,数组的索引就好象饭店房间的编号一样,想要找到某个房间时,就得先找到房间编号!接下来再举一个例子,说明如何将数组里的最大值及最小值列出。
算法思路:
打擂台方法:将变量min与max初值设成数组的第一个元素后,再逐一与数组中的各元素相比。比min小,就将该元素的值指定给min存放,使min的内容保持最小;同样的,当该元素比max大时,就将该元素的值指定给max存放,使max的内容保持最大。for循环执行完,也就表示数组中所有的元素都比较完毕,此时变量min与max的内容就是最小值与最大值。
范例:TestJava3_3.java
//这个程序主要是求得数组中的最大值和最小值 public class TestJava3_3 { public static void main(String args[]) { int i, min, max; int A[] = {74, 48, 30, 17, 62}; // 声明整数数组A,并赋初值 min = max = A[0]; System.out.print("数组A的元素包括:"); for(i=0; i<A.length; i++) { System.out.print(A[i]+" "); if(A[i] > max) // 判断最大值 max = A[i]; if(A[i] < min) // 判断最小值 min = A[i]; } System.out.println(" 数组的最大值是:"+max); // 输出最大值 System.out.println("数组的最小值是:"+min); // 输出最小值 } }
输出结果:
数组A的元素包括:74 48 30 17 62
数组的最大值是:74
数组的最小值是:17
程序说明:
1、第6行声明整数变量i 做为循环控制变量及数组的索引,另外也声明存放最小值的变量min与最大值的变量max。
2、第7行声明整型数组A,其数组元素有5个,其值分别为74、48、30、17、62。
3、第8行将min与max的初值设为数组的第一个元素。
4、第9~17行逐一输出数组里的内容,并判断数组里的最大值与最小值。
5、第18~19行输出比较后的最大值与最小值。
3.1.5 与数组操作有关的 API 方法
在Java语言中提供了许多的API方法,供开发人员使用,java提供的java.util 包里面的Arrays类包含一些静态修饰方法可以直接操作数组。比如:
binarySearch:使用二分搜索法出现的索引位置、如果没有搜索到就返回一个负数
copyOf:复制指定的数组,截取或用0填充
copyOfRange:将指定数组的指定范围复制到一个新数组
equals:如果两个指定的数据数组彼此相等
fill:将指定的值分配给指定数组的每个元素
sort:对指定的数组按数字升序进行排序
toString:将数组里面的元素用英文逗号连接成字符串
下面先介绍两种常用的数组操作方法,一个是数组的拷贝操作,另一个是数组的排序操作。其它的操作请自行查阅JDK帮助文档。
范例:TestJava3_4.java
//以下这段程序说明数组的拷贝操作 public class TestJava3_4 { public static void main(String[] args) { int a1[] = {1, 2, 3, 4, 5}; // 声明两个整型数组a1、a2,并进行静态初始化 int a2[] = {9, 8, 7, 6, 5, 4, 3}; System.arraycopy(a1, 0, a2, 0, 3); // 执行数组拷贝的操作 System.out.print("a1数组中的内容:"); for(int i=0; i<a1.length; i++) // 输出a1数组中的内容 System.out.print(a1[i]+" ");
System.out.println(); System.out.print("a2数组中的内容:");
for(int i=0; i<a2.length; i++) // 输出a2数组中的内容 System.out.print(a2[i] +" ");
System.out.println(" 数组拷贝完成!"); } }
输出结果:
a1数组中的内容:1 2 3 4 5
a2数组中的内容:1 2 3 6 5 4 3
数组拷贝完成!
System.arraycopy(source, 0, dest, 0, x);语句的意思就是:复制源数组从下标0开始的x个元素到目标数组,从目标数组的下标0所对应的位置开始存取。
范例:TestJava3_5.java
//以下程序是数组的排序操作,在这里使用了sort方法对数组进行排序 import java.util.*; public class TestJava3_5 { public static void main(String[] args) { int a[] = {4, 32, 45, 32, 65, 32, 2}; System.out.print("数组排序前的顺序:"); for(int i=0; i<a.length; i++) System.out.print(a[i]+" ");
Arrays.sort(a); // 数组的排序方法 System.out.print(" 数组排序后的顺序:");
for(int i=0; i<a.length; i++) System.out.print(a[i]+" "); } }
输出结果:
数组排序前的顺序:4 32 45 32 65 32 2
数组排序后的顺序:2 4 32 32 32 45 65
程序第12行的Arrays.sort(数组名)为数组排序的操作,但这个方法在java.util这个包里面,所以在用到的时候需要先将它导入,至于包的概念,以后章节会讲到。
·binarySearch
以int型为例,其它数据类型类似。
函数原型如下:
public static int binarySearch(int[] a, int key);
使用二分搜索法来搜索指定的int型数组,以获得指定的值。必须在进行此调用之前对数组进行排序(通过 sort(int[]) 方法)。如果没有对数组进行排序,则结果是负的。如果数组包含多个带有指定值的元素,则返回的是第一个找到的下标。
public static int binarySearch(int[] a, int fromIndex, int toIndex, int key);
使用二分搜索法来搜索指定的int型数组的范围,以获得指定的值。必须在进行此调用之前对范围进行排序(通过 sort(int[], int, int) 方法)。如果没有对范围进行排序,则结果是负的。如果范围包含多个带有指定值的元素,则返回的是第一个找到的下标。
参数:
a - 要搜索的数组
fromIndex - 要搜索的第一个元素的索引(包括)
toIndex - 要搜索的最后一个元素的索引(不包括)
key - 要搜索的值
返回:
如果它包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。插入点被定义为将键插入数组的那一点:即第一个大于此键的元素索引,如果数组中的所有元素都小于指定的键,则为 a.length。注意,这保证了当且仅当此键被找到时,返回的值将 >= 0。
实例如下:
int iNum = 3; int A[] = {4, 9, 7, 8, 2, 1, 5, 6, 3}; Arrays.sort(A); int index = Arrays.binarySearch(A, iNum); //或: // Arrays.sort(A, 0, A.length); //int index = Arrays.binarySearch(A, 0, A.length, iNum);//[0, A.length) System.out.println(index); if(index < 0) System.out.println("找不到该元素~"); else System.out.println("找到,元素的下标为:" + index);
·copyOf
以int型为例,其它数据类型类似。
函数原型如下:
public static int[] copyOf(int[] original, int newLength);
复制指定的数组,截取或用0填充(如有必要),以使副本具有指定的长度。对于在原数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于在副本中有效而在原数组无效的所有索引,副本将包含0。当且仅当指定长度大于原数组的长度时,这些索引存在。
参数:
original - 要复制的数组
newLength - 要返回的副本的长度
返回:
原数组的副本,截取或用 0 填充以获得指定的长度
抛出:
NegativeArraySizeException - 如果 newLength 为负
NullPointerException - 如果 original 为 null
实例如下:
int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int B[] = Arrays.copyOf(A, 14); for(int iVal : B) System.out.println(iVal);
·copyOfRange
以int型为例,其它数据类型类似。
函数原型如下:
public static int[] copyOfRange(int[] original, int from, int to);
将指定数组的指定范围复制到一个新数组。该范围的初始索引(from)必须位于0和original.length(包括)之间。original[from]处的值放入副本的初始元素中(除非from == original.length或from == to)。原数组中后续元素的值放入副本的后续元素。该范围的最后索引to(必须大于等于from)可以大于original.length,在这种情况下,0被放入索引大于等于original.length - from的副本的所有元素中。返回数组的长度为to - from。
参数:
original - 将要从其复制一个范围的数组
from - 要复制的范围的初始索引(包括)
to - 要复制的范围的最后索引(不包括)。(此索引可以位于数组范围之外)。
返回:
包含取自原数组指定范围的新数组,截取或用 0 填充以获得所需长度
抛出:
ArrayIndexOutOfBoundsException - 如果 from < 0 或 from > original.length()
IllegalArgumentException - 如果from > to
NullPointerException - 如果original为null
实例如下:
int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int B[] = Arrays.copyOfRange(A, 2, 14); for(int iVal : B) System.out.println(iVal);
·equals
以int型为例,其它数据类型类似。
函数原型如下:
public static boolean equals(int[] a, int[] a2);
如果两个指定的int型数组彼此相等,则返回true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。此外,如果两个数组引用都为null,则认为它们是相等的。
参数:
a - 将测试其相等性的一个数组
a2 - 将测试其相等性的另一个数组
返回:
如果两个数组相等,则返回 true
示例如下:
int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int B[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; System.out.println(Arrays.equals(A, B));
·fill
以int型为例,其它数据类型类似。
函数原型如下:
public static void fill(int[] a, int val)
将指定的int值分配给指定int型数组的每个元素。
public static void fill(int[] a, int fromIndex, int toIndex, int val)
将指定的int值分配给指定int型数组指定范围中的每个元素。填充的范围从索引 fromIndex(包括)一直到索引 toIndex(不包括)。(如果 fromIndex==toIndex,则填充范围为空。)
参数:
a - 要填充的数组
fromIndex - 要使用指定值填充的第一个元素的索引(包括)
toIndex - 要使用指定值填充的最后一个元素的索引(不包括)
val - 要存储在数组所有元素中的值
抛出异常:
IllegalArgumentException - 如果 fromIndex > toIndex
ArrayIndexOutOfBoundsException - 如果 fromIndex < 0 或 toIndex > a.length
实例如下:
int A[] = new int[10]; Arrays.fill(A, 1); //或:Arrays.fill(A, 0, A.length, 1); for(int iVal : A) System.out.println(iVal);
·toString
以int型为例,其它数据类型类似。
函数原型如下:
public static String toString(int[] a);
返回指定数组内容的字符串表示形式。字符串表示形式由数组的元素列表组成,括在方括号("[]")中。相邻元素用字符", "(逗号加空格)分隔。这些元素通过 String.valueOf(int) 转换为字符串。如果a为 null,则返回 "null"。
参数:
a - 返回其字符串表示形式的数组
返回:
a 的字符串表示形式
实例如下:
int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; String str = Arrays.toString(A); System.out.println(str);
注意:可以利用String.valueOf(int)来实现Arrays.toString相同的功能,如下:
String str = "[" + String.valueOf(A[0]) + ", " + String.valueOf(A[1]) + "]";
3.2 二维数组
虽然一维数组可以处理一般简单的数据,但是在实际的应用上仍显不足,所以Java也提供了二维数组以及多维数组供程序设计人员使用。学会了如何使用一维数组后,再来看看二维数组的使用方法。
3.2.1 二维数组的声明与分配内存
二维数据就是一个表格或一个矩阵,可以把它想象成一个行列表。
二维数组声明的方式和一维数组类似,内存的分配也一样是用new这个关键字。其声明与分配内存的格式如下所示:
数据类型 数组名[][];或数据类型[][] 数组名;
数组名 = new 数据类型[行的个数][列的个数];
“行的个数”是告诉编译器所声明的数组有多少行,“列的个数”则是说明该数组有多少列,如下面的范例:
int score[][] ; // 声明整型数组score
score = new int[4][3] ; // 分配一块内存空间,供4行3列的整型数组score使用
同样的,可以用较为简洁的方式来声明数组,其格式如下:
数据类型 数组名[][] = new 数据类型[行的个数][列的个数];
或:
数据类型[][] 数组名 = new 数据类型[行的个数][列的个数];
若用上述的写法,则是在声明的同时,就开辟了一块内存空间,以供该数组使用。范例如下:
int score[][] = new int[4][3] ; //声明整型数组score,同时为其开辟一块内存空间
上面的语句中,整型数据score可保存的元素有4*3=12个,而在Java中,int数据类型所占用的空间为4个字节,因此该整型数组占用的内存共为4*12=48个字节。则此时的二维数组相当于一个4行3列的二维表,如图3-5:
图3-5 二维表
如果想直接在声明时就为数组赋初值,可以利用大括号完成。只要在数组的声明格式后面再加上所赋初值即可,如下面的格式:
数据类型 数组名[][] = new 数据类型[][]{
{第0行初值},
{第1行初值},
…,
{第n行初值},
};
或简写成:
数据类型 数组名[][] = {
{第0行初值},
{第1行初值},
…,
{第n行初值},
};
要特别注意的是,用户不需要定义数组的长度,因此在数组名后面的中括号里不必填入任何的内容。此外,在大括号内还有几组大括号,每组大括号内的初值会依序指定给数组的第0、1、…、n行元素。如下面的关于数组num声明及赋初值的范例:
int num[][] = new int[][]{ // 二维数组的初始化 {23, 45, 21, 45}, {45, 23, 46, 23} }; 或: int num[][] = { // 二维数组的初始化 {23, 45, 21, 45}, {45, 23, 46, 23} };
在上面的语句中,声明了一个整型数组num,数组有2行4列共8个元素,大括号里的几组初值会分别依序指定给各行里的元素存放,num[0][0]为23,num[0][1]为45,…,num[1][3]为23。
3.2.1.1 每行的元素个数不同的二维数组
值得一提的是Java允许二维数组中每行的元素个数均不相同,这点与一般的程序语言是不同的。例如,下面的语句是声明整型数组num并赋初值,而初值的赋值指明了num具有三行元素,其中第一行有4个元素,第二行有3个元素,第三行则有5个元素:
int num[][] = { {42, 54, 34, 67}, {33, 34, 56}, {12, 34, 56, 78, 90} }; 或采用下面的方法动态初始化: int a[][] = new int[4][];//列利用循环动态分配 for(int i = 0; i < a.length; i++) { a[i] = new int[i+2]; for(int j = 0; j < a[i].length; j++) a[i][j] = i*j; }
3.2.1.2 取得二维数组的行数与特定行的元素的个数
在二维数组中,若是想取得整个数组的行数,或者是某行元素的个数时,可利用“.length”来获取,其语法如下:
数组名.length // 取得数组的行数
数组名[行的索引].length // 取得特定行元素的个数
也就是说,如要取得二维数组的行数,只要用数组名加上“.length”即可;如要取得数组中特定行的元素的个数,则须在数组名后面加上该行的索引值,再加上“.length”,如下面的程序片段:
num.length; // 计算数组num的行数,其值为3
num[0].length // 计算数组num的第1行元素的个数,其值为4
num[2].length // 计算数组num的第3行元素的个数,其值为5
3.2.2 二维数组元素的引用及访问
二维数组元素的输入与输出方式与一维数组相同,看下面这个范例:
范例:TestJava3_6.java
//二维数组的使用说明,这里采用静态赋值的方式 public class TestJava3_6 {
public static void main(String args[]) { int i, j, sum = 0; int num[][] = {{30, 35, 26, 32}, {33, 34, 30, 29}}; // 声明数组并设置初值
for(i=0; i<num.length; i++) { System.out.print("第"+(i+1)+" 个人的成绩为:"); for(j=0; j<num[i].length; j++) { System.out.print(num[i][j]+" "); sum += num[i][j]; } System.out.println(); } System.out.println(" 总成绩是"+sum+" 分!"); } }
输出结果:
第1 个人的成绩为:30 35 26 32
第2 个人的成绩为:33 34 30 29
总成绩是249 分!
程序说明:
1、第6行声明整数变量i、j 做为外层与内层循环控制变量及数组的索引,i 控制行的元素,j 控制列的元素;而sum则使用来存放所有数组元素值的和,也就是总成绩。
2、第7行声明一整型数组num,并为数组元素赋初值,该整型数组共有8个元素。
3、第8~17行输出数组里各元素的内容,并进行成绩汇总。
4、第18行输出sum的结果即为总分。
与一维数组类似,也可以采用for_each结构输出二维数组的所有元素:
for(int[] i : num) { for(int iVal : i) { System.out.print(iVal + " "); sum += iVal; } System.out.println(); } System.out.println(" 总成绩是"+sum+" 分!");
感谢阅读。如果感觉此章对您有帮助,却又不想白瞟