java 数 组


6.1
一维数组
6.2
一维数组引用举例
*
6.3
二维数组
*
6.4
二维数组的引用

6.1




6.1.1
一维数组的声明
声明一个数组就是要确定数组名、数组的维数和数组元素的
数据类型。数组名是符合Java标识符定义规则的用户自定义标识
符。数组的维数用方括号(“[ ]”)的个数来确定,对于一维数组来
说,只需要一对方括号。数组元素的数据类型可以是Java的任何
数据类型,如基本类型(int、float、double、char等),类(class)或
接口(interface)等。数组的声明格式如下:

类型标识符 数组名[ ];

类型标识符[ ] 数组名;
例如:
int abc[ ];
double[ ] example2;
都是正确的一维数组声明语句。

6.1.2
一维数组的初始化
声明一个数组仅仅为这个数组指定数组名和数组元素的类
型,并不为数组元素分配实际的存储空间。这是因为Java的数组
声明中并未指出数组元素的个数,系统无法知道需要给这个数组
分配多大的内存空间。要想使一个数组占有所需要的内存空间,
必须指出数组元素的个数,在Java程序中把这一工作放在数组的
初始化时进行。数组经过初始化后,其长度(即可存放的元素个
数)就不可再改变。Java数组的初始化可以通过直接指定初值的方
式来完成,也可以用new操作符来完成。

1
.直接指定初值的方式
用直接指定初值的方式对数组初始化,是在声明一个数组
的同时将数组元素的初值依次写入赋值号后的一对花括号内,
给这个数组的所有数组元素赋上初始值。这样,Java编译器可
通过初值的个数确定数组元素的个数,为它分配足够的存储空
间并将这些值写入相应的存储单元。例如:
int[ ] a1={23,-9,38,8,65};

这条语句声明数组名为a1,数组元素的数据类型为整型
(int,占4个字节),共有5个初始值,故数组元素的个数为5。这
样一个语句为Java分配存储空间提供了所需要的全部信息,系
统可为这个数组分配5*4=20个字节的连续存储空间。经过初始
化后,使a1[0]=23,a1[1]=-9,a1[2]=38, a1[3]= 8,a1[4]= 65。
其存储空间分配及各数组元素的初始值如图6.1所示。注意Java
中的数组下标从0开始。

图6.1 数组a1的初始化
数组元素a1[0] a1[1] a1[2] a1[3] a1[4]
初始值23 –9 38 8 65

2
.用关键字new
初始化数组
用关键字new初始化数组,只为数组分配存储空间而不对数
组元素赋初值。用关键字new来初始化数组有两种方式:
(1) 先声明数组,再初始化数组。这实际上由两条语句构
成,格式如下:
类型标识符
数组名[ ]

数组名=new
类型标识符[
数组长度]

其中,第一条语句是数组的声明语句,第二条语句是初始化语句。
应该注意的是:两条语句中的数组名、类型标识符必须一致。数
组长度通常是整型常量,用以指明数组元素的个数。例如:

图6.2 用new关键字对数组a初始化
int a[ ];
a=new int[9];
a[5] a[6] a[7] a[8]
数组元素a[0] a[1] a[2] a[3] a[4]

(2) 在声明数组的同时用new关键字初始化数组。这种初始
化实际上是将上面所述的两条语句合并为一条语句罢了。格式
如下:
类型标识符 数组名[ ]=new 类型标识符[数组长度];

类型标识符[ ] 数组名=new 类型标识符[数组长度];
例如:
int[ ] a=new int[10];

6.1.3
一维数组的引用
当数组初始化后就可通过数组名与下标来引用数组中的每
一个元素。一维数组元素的引用格式如下:
数组名[数组下标]
其中:数组名是经过声明和初始化的标识符;数组下标是指元
素在数组中的位置,数组下标的取值范围是0~(数组长度-1),
下标值可以是整数型常量或整数型变量表达式。例如,在有了
“int[ ] a=new int[10];”声明语句后,下面的两条赋值语句是合法
的:

a[3]=25;
a[3+6]=90;

a[10]=8;
是错误的。这是因为Java为了保证安全性,要对引用时的数组元
素进行下标是否越界的检查。这里的数组a在初始化时确定其长
度为10,下标从0开始到9正好10个元素,因此,不存在下标为
10的数组元素a[10]。

6.2
一维数组引用举例
6.2.1
测定数组的长度
在Java语言中,数组也是一种对象。数组经初始化后就确定
了它的长度,对于每个已分配了存储空间的数组,Java用一个数
据成员length来存储这个数组的长度值。
【示例程序c6_1.java】 数组的声明、初始化和其长度的测定。
public class c6_1
{
public static void main(String arg[ ])
{

int i;
double a1[ ];//[ ]放在变量后面声明
char[ ] a2; //[ ]放在变量前面声明
a1=new double[8];//为a1分配8个double型元素的存储空间(64字节)
a2=new char[8]; //为a2分配8个char型元素的存储空间(16字节)
int a3[ ]=new int[8];//在声明数组时初始化数组, 为a3分配32字节
byte[ ] a4=new byte[8]; //在声明数组时初始化数组,为a4分配8字节
char a5[ ]={'A','B','C','D','E','F','H','I'};//直接指定初值方式
//下面各句测定各数组的长度

System.out.println("a1.length="+a1.length);
System.out.println("a2.length="+a2.length);
System.out.println("a3.length="+a3.length);
System.out.println("a4.length="+a4.length);
System.out.println("a5.length="+a5.length);
//以下各句引用数组中的每一个元素,为各元素赋值
for(i=0;i<8;i++)
{ a1[i]=100.0+i;
a3[i]=i;
a2[i]=(char)(i+97);//将整型转换为字符型
}

//下面各句打印各数组元素
System.out.println("/ta1/ta2/ta3/ta4/ta5");
System.out.println("/tdouble/tchar/tint/tbyte/tchar");
for(i=0;i<8;i++)
System.out.println("/t"+a1[i]+"/t"+a2[i]+"/t"+
a3[i]+"/t"+a4[i]+"/t"+a5[i]);
}
}

该程序的运行结果如下:
a1.length=8
a2.length=8
a3.length=8
a4.length=8
a5.length=8

a1 a2 a3 a4 a5
double char int byte char
100.0 a 0 0 A
101.0 b 1 0 B
102.0 c 2 0 C
103.0 d 3 0 D
104.0 e 4 0 E
105.0 f 5 0 F
106.0 g 6 0 H
107.0 h 7 0 I

6.2.2
数组下标的灵活使用
我们在本章开头提出的计算30名学生平均成绩问题的关键
是一般的变量标识符中没有可变的东西,而数组作为一组变量
的代表者其下标可以使用变量。实际上,我们在示例程序
c6_1.java中已经用到了数组下标的这一特性。下面我们再通过
几个例子来说明数组下标的灵活使用。
【示例程序c6_2.java】 用数组求解Fibonacci数列的前20
项,即使用数组下标表达式求解数学上的迭代问题。

public class c6_2
{
public static void main(String[ ] args)
{ int i;
int f[ ]=new int[20];//创建数组f,使其可存储20个整型数据
f[0]=1;f[1]=1;
for(i=2;i<20;i++)
f[i]=f[i-2]+f[i-1];//数组元素的下标使用循环变量
for(i=0;i<20;i++)
{ if(i%5==0)System.out.println("/n");
System.out.print("/t"+f[i]);
}
}
}

运行结果是:
1 1 2 3 5
8 13 21 34 55
89 144 233 377 610
987 1597 2584 4181 6765

在日常生活中,人们几乎每天都要进行“查找”。例如,在电
话号码簿中查找某单位或某人的电话号码;在字典中查阅某个
词的读音和含义等。查找的关键问题是如何快速地找到待查的
内容。例如,查字典的关键是如何快速地确定待查之字在字典
的哪一页。对于浩如烟海的计算机中的数据,有相当多的数据
是以数组的形式组织与存放的。以数组的形式组织和存放数据
的数据结构被称为顺序表。对于顺序表的查找,人们已经发明
了许多种算法,典型的有顺序查找和二分(折半、对分)查找。
顺序查找是将待查值与顺序表(数组)中的每个元素逐一比
较,直至查找成功或到达数组的最后一个元素还未找到。这种
查找的效率相对较低。

二分查找是在一个有序表(数据是按其值由小到大或由大到
小依次存放的,这里我们以值由小到大排列为例)中,每次都与
中间的那个元素比较,若相等则查找成功;否则,调整查找范
围,若中间那个元素的值小于待查值,则在表的后一半中查
找;若中间那个元素的值大于待查值,则在表的前一半中查
找;如此循环,每次只与一半中的一个元素比较,可使查找效
率大大提高。

【示例程序c6_3.java】 设数组中的数值是由小到大存放
的,编写二分查找程序。
import java.io.*;
class FindSearch
{ int binarySearch(int arr[ ],int searchValue)
{ int low=0;// low是第一个数组元素的下标
int high=arr.length-1;// high是最后一个数组元素的下标
int mid=(low+high)/2; // mid是中间那个数组元素的下标
while(low<=high && arr[mid]!=searchValue)
{ if( arr[mid]<searchValue)
low=mid+1;//要找的数可能在数组的后半部分中

else
high=mid-1; //要找的数可能在数组的前半部分中
mid=(low+high)/2;
}
if(low>high) mid=-1;
return mid;// mid是数组元素下标,若为-1则表示不存在要查的元素
}
}
public class c6_3
{
public static void main(String[ ] args)throws IOException

{ BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
int i,k,search,mid;
String c1;
int arr[ ]={2,4,7,18,25,34,56,68,89};
System.out.println("打印原始数据");
for(i=0;i<arr.length;i++) System.out.print(" "+arr[i]);
System.out.println("/n");
System.out.println("请输入要查找的整数");
c1=keyin.readLine( );

search=Integer.parseInt(c1);
FindSearch p1=new FindSearch( );
mid=p1.binarySearch(arr,search);
if(mid==-1) System.out.println("没找到!");
else System.out.println("所查整数在数组中的位置下标是:"+mid);
}
}

运行结果:
打印原始数据
2 4 7 18 25 34 56 68 89
请输入要查找的整数 68
所查整数在数组中的位置下标是:7

这个程序的查找过程及其查找元素的位置(数组下标)的变
化如图6.3所示。在这个程序中,为了让读者能从键盘输入“整
数”(这里对整数二字加引号是因为Java把键盘的任何输入都当
成字符流来处理),使用了尚未学到的输入输出流语句:
BufferedReader keyin=new BufferedReader(new
InputStreamReader(System.in));
关于输入输出流的知识请参阅第12章“文件和流”,这里只简要
说明如下:

Java语言中的输入输出有三种状态:标准输入状态
System.in、标准输出状态System.out和标准错误状态System.err。
在Java内输入一个字符串需使用readLine( )方法。要使用
readLine( )方法,必须从InputStreamReader内建立
BufferedReader对象。InputStreamReader用来建立一个使用预设
字符编码的InputStreamReader对象。
InputStreamReader(System.in)表示InputStreamReader对象是由
System.in建立的。BufferedReader用来建立一个固定内存大小
的字符输入缓冲流。

图6.3 二分查找的比较与下标调整过程

6.2.3
数组间相互赋值
Java语言允许类型相同,而长度不同的两个数组互相赋值。
赋值的结果是两个数组名指向同一数组。
【示例程序c6_4.java】 编程实现两个数组间相互赋值。
public class c6_4
{
public static void main(String arg[ ])
{ int i;
int[ ] a1={2,5,8,25,36};
int a3[ ]={90,3,9};
System.out.println("a1.length="+a1.length);

System.out.println("a3.length="+a3.length);
a3=a1; //赋值的结果是a3指向a1指向的数组
System.out.print("a1:");
for(i=0;i<a1.length;i++)
System.out.print(" "+a1[i]);
System.out.println("/n");
System.out.println("a3.length="+a3.length);
System.out.print("a3:");
for(i=0;i<a3.length;i++)
System.out.print(" "+a3[i]);
System.out.println("/n");
}
}

运行结果:
a1.length=5
a3.length=3
a1: 2 5 8 25 36
a3.length=5
a3: 2 5 8 25 36

6.2.4
向成员方法传递数组元素
向成员方法传递数组元素也就是用数组元素作为成员方法
的实参。由于实参可以是表达式,而数组元素可以是表达式的
组成部分,因此,数组元素可以作为成员方法的实参。数组元
素作为成员方法的实参与用变量作为实参一样,是单向值传
递,即只能由数组元素传递给形参,程序中对形参的任何修改
并不改变数组元素的值。
【示例程序c6_5.java】 数组元素作为成员方法的实参,在
成员方法中改变形参x和y的值,方法调用结束后实参数组元素
的值没有改变。

class ff
{ int aa(int x,int y)//定义方法aa,有两个整型形参x和y
{ int z;
x=x+4;y=y+2; z=x*y; return z;
}
}
public class c6_5
{
public static void main(String[ ] args)
{ int arr[ ]={6,8,9}; //声明并初始化数组arr

int len=arr.length, k;
ff p1=new ff( );
k=p1.aa(arr[0],arr[1]);//数组元素arr[0]和arr[1]作为方法aa的实参
System.out.println("k="+k);
for(int i=0;i<len;i++)
System.out.print(" "+arr[i]);//循环输出数组元素的值
System.out.println("/n");
}
}
运行结果:
k=100
6 8 9

6.2.5
向成员方法传递数组
在定义成员方法时可以用数组作为它的形参,且只指定数
组名和它的数据类型,而不指定数组的长度。在这种情况下引
用该成员方法时,必须用具有相同数据类型的数组名作为成员
方法对应位置的实参,即向成员方法传递数组。更应强调的是:
数组名作为成员方法的实参时,是把实参数组的起始地址传递
给形参数组,即两个数组共用同一段内存单元,因此,在成员
方法中对形参数组中各元素值的修改,都会使实参数组元素的
值也发生同样的变化。这种参数的传递方式被称为“双向地址传
递”。
【示例程序c6_6.java】 两个数组相加,将结果存入第二个
数组中。

class Add1Class
{ void add(int arA[ ],int arB[ ])
{ int i;
int len=arA.length;
for(i=0;i<len;i++)
arB[i]=arA[i]+arB[i];
}
}
public class c6_6
{ public static void main(String[ ] args)

{ int i,k;
int arX[ ]={1,3,7,6};
int arY[ ]={78,0,42,5};
int len=arX.length;
Add1Class p1=new Add1Class( );
System.out.println(" arX的原始数据");//打印X数组
for(i=0;i<len;i++)
System.out.print(" "+arX[i]);
System.out.println("/n arY的原始数据");//打印Y数组
for(i=0;i<len;i++)
System.out.print(" "+arY[i]);

p1.add(arX,arY); //调用add方法计算两个数组之和
System.out.println("/n 再次输出arX");//再次打印X数组
for(i=0;i<len;i++)
System.out.print(" "+arX[i]);
System.out.println("/n 再次输出arY");//再次打印Y数组
for(i=0;i<len;i++)
System.out.print(" "+arY[i]);
System.out.println("/n");
}
}

运行结果如下:
arX的原始数据
1 3 7 6
arY的原始数据
78 0 42 5
再次输出arX
1 3 7 6
再次输出arY
79 3 49 11

从程序的执行结果中可以看出,数组arY的值在调用成员方
法add前后是不同的。这是因为成员方法add中的形参数组arB与
实参数组arY共用同一块存储单元,因此,在成员方法add中对
形参数组arB各数组元素值的修改,也就是对实参数组arY中各
数组元素的修改。这一过程如图6.4所示。

图6.4 数组名用作实参的“双向地址传递”
arX
与arY
共用
同一块存储单元
0
1
2
3
arY
与arB
共用
同一块存储单元
0
1
2
3
1
3
7
6
78
0
42
5
arX
与arY
共用
同一块存储单元0
1
2
3
对arB
的修改
也是对arY
的修改0
1
2
3
开始调用成员方法add时
成员方法add调用结束时

6.2.6
同类型不同长度的数组引用同一个成员方法
在Java语言中,数组也是一种对象,因此,只要数组元素
的数据类型相同,而不论其长度是否一致,都可以引用同一个
成员方法,完成所需要的操作。
【示例程序c6_7.java】 有两个一维数组s1和s2,s1存放着8
名学生的成绩,s2存放着5名学生的成绩,分别求出这两组学生
的平均成绩。

public class c6_7
{ static double average(double ascore[ ])
{ double aaver=0;
for(int i=0;i<ascore.length;i++) aaver=aaver+ascore[i];
aaver=aaver/ascore.length;
return aaver;
}
public static void main(String arg[ ])

{ int i; double aver1,aver2;
double s1[ ]={90,56,86.5,87,99,67.5,65,80};
double s2[ ]={70,90,87,99,67};
System.out.println("s1.length="+s1.length);
aver1=average(s1);//数组s1作为average成员方法的实参
System.out.println("aver1="+aver1);
System.out.println("s2.length="+s2.length);
aver2=average(s2); //数组s2作为average成员方法的实参
System.out.println("aver2="+aver2);
}
}

运行结果:
s1.length=8
aver1=78.875
s2.length=5
aver2=82.6

在这个程序中,尽管两个数组的长度不同(分别为8和5),但
其数据类型相同,因此,可以引用同一个成员方法(计算平均成
绩)来完成所需要的操作。这是由于在定义该成员方法时要求以
数组作为参数。我们已经知道,数组名作为成员方法的实参
时,是把实参数组的起始地址连同它所占据的存储空间传递给
形参数组,使形参数组与实参数组共用同一段内存单元,实参
数组的长度也就是形参数组的长度,因此,不论该实参数组的
长度如何,只要其数据类型相同,都可以引用同一个成员方法。

6.2.7
数组元素排序
排序是把一组数据按照值的递增(由小到大,也称为升序)
或递减(由大到小,也称为降序)的次序重新排列的过程,它是
数据处理中极其常用的运算。利用数组的顺序存储特点,可方
便的实现排序。排序算法有多种,这里只讨论较易理解的冒泡
排序和选择排序两种,且要求排序结果为升序。

冒泡排序的关键点是从后向前对相邻的两个数组元素进行
比较,若后面元素的值小于前面元素的值,则将这两个元素交
换位置,否则不进行交换。依次进行下去,第一趟排序可将数
组中值最小的元素移至下标为0的位置。对于有n个元素的数
组,循环执行n-1趟扫描便可完成排序。(当然,也可以从前向
后对相邻的两个数组元素进行比较,但此时应注意将大数向后
移,与小者前移的冒泡法相对应,可将这种大者后移的排序称
为下沉法)。图6.5演示了有6个元素的数组实施冒泡法排序(小数
前移)的前两趟比较与交换过程。可以看出,第一趟排序后最小
数12已移到了下标为0的正确位置;第二趟排序后次小数17移到
了下标为1的正确位置。

图6.5 冒泡法对6个数据排序的两趟扫描中比较与交换的过程

【示例程序c6_8.java】 用冒泡法对8个数从小到大排序。
import java.io.*;
class SortClass//类定义开始
{ void sort(int arr[ ])//开始定义冒泡排序方法sort
{ int i,k,temp;
int len=arr.length;
for(i=0;i<len-1;i++)
for(k=len-1;k>i;k--)
if( arr[k]<arr[k-1])
{ temp=arr[k-1];

arr[k-1]=arr[k];
arr[k]=temp;
}//if块结束,同时使内循环for(k……)和外循环for(i……)结束
}//sort方法结束
}//类SortClass定义结束
public class c6_8
{
public static void main(String[ ] args)throws IOException

{ BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
int i,k,temp;
String c1;
int arr[ ]=new int[8];
int len=arr.length;
System.out.println(" 请从键盘输入8个整数,一行只输入一个数" );
for(i=0;i<len;i++)
{ c1=keyin.readLine( ); //用于读取一个字符串
arr[i]=Integer.parseInt(c1);//将字符串类型c1转换成整数类型
}

//打印原始数据
System.out.print ("原始数据:");
for(i=0;i<len;i++)
System.out.print(" "+arr[i]);
System.out.println("/n");
SortClass p1=new SortClass( );
p1.sort(arr);//实参为数组名
System.out.println("冒泡法排序的结果:");
for(i=0;i<len;i++)
System.out.print(" "+arr[i]);
System.out.println("/n");
}
}

程序的运行结果如下:
请从键盘输入8个整数,一行只输入一个数
2
34
……(其余输入从略)
原始数据: 2 34 0 9 -1 -6 45 23
冒泡法排序的结果:
-6 -1 0 2 9 23 34 45

冒泡法排序相对比较容易理解,但排序过程中元素的交换
次数较多,特殊情况下每次比较都要进行交换(例如,若要将以
降序排列的数据9,8,7,6,5,4重新以升序排列成4,5,6,7,8,9)。而选
择法排序每执行一次外循环只进行一次数组元素的交换,可使
交换的次数大大减少。
选择法排序的基本思想是首先从待排序的n个数中找出最小
的一个与arr1[0]对换;再将arr1[1]到arr1[n]中的最小数与arr1[1]
对换,依此类推。每比较一轮,找出待排序数中最小的一个数
进行交换,共进行n-1次交换便可完成排序。图6.6演示了这一过
程。

图6.6 选择法排序的交换过程

【示例程序c6_9.java】 选择法排序。
class SelectSort
{
static void sort(int arr1[ ]) //成员方法的形参是数组
{
int i,j,k,t;
int len=arr1.length;
for(i=0;i<len-1;i++) //外循环开始
{
k=i;
for(j=i+1;j<len;j++)
if( arr1[j]<arr1[k]) k=j;//内循环只用k记录最小值的下标

if(k>i)
{ t=arr1[i]; //在外循环实施交换,可减少交换次数
arr1[i]=arr1[k];
arr1[k]=t;
}// if(k>i)结束
}//外循环for(i……)结束
}//成员方法sort定义毕
}
public class c6_9 extends SelectSort
{
public static void main(String[ ] args)
{

int arr[ ]={78,70,2,5,-98,7,10,-1};
int len=arr.length;
SelectSort.sort(arr);//数组名作为成员方法的实参
System.out.print("选择法排序的结果:");
System.out.println("length="+arr.length);
for(int i=0;i<len;i++)
System.out.print(" "+arr[i]);//数组arr的值已在方法调用中改变了
System.out.println("/n");
}
} 运行结果如下:
选择法排序的结果: -98 -1 2 5 7 10 70 78

6.2.8
对象数组
前面讨论的数组的数据类型都是简单的基本类型,即数组
元素是简单数据类型。但实际问题中往往需要把不同类型的数
据组合成一个有机的整体,以便于引用。例如,一名学生的姓
名、性别、年龄和各科学习成绩等都与这名学生紧密相关,而
一个班(乃至更多)的学生又都具有这些属性,如表6.1所示。如
果将这些数据项定义为相互独立的简单变量是难以反映它们之
间的内在联系的,应当将它们组织在一起形成一种数据结构。
这种数据结构在过去的结构化程序设计中被称为记录或结构
体,而在面向对象的程序设计中把这种结构也称为对象。

表6.1





姓名性别年龄数学英语计算机
Li F 19 89.0 86 69
He M 18 90.0 83 76
Zhang M 20 78.0 91 80
…… …… …… …… …… ……

由自定义的对象作为数组元素所构成的数组被称为对象数
组,也就是说,对象数组是指数组的每一个元素都是具有相同属
性的对象,或者说,如果一个类有若干个对象,我们可以把这一
系列同类的对象用一个数组来存放。
【示例程序c6_10.java】 设有若干名学生,每个学生有姓名、
性别和成绩三个属性,要求将每个学生作为一个对象,建立获取
对象名字的成员方法getName和获取对象性别的成员方法
getSex,以及输出对象的全部数据成员的成员方法studPrint。

class Student
{ private String name;
private char sex;
private double score;
Student(String cname,char csex,double cscore)
{ name=cname;
sex=csex;
score=cscore;
}

String getName( ){return name;}
char getSex( ){return sex;}
void studPrint( )
{ System.out.println("Name: "+name+"/tSex: "+sex+"/tScore: "+score);}
}
public class c6_10
{
public static void main(String[ ] args)

{ String mname;
char msex;
int len;
//声明对象数组,声明对象数组时不为每一个对象数组的元素分配存储空间
Student[ ] st1=new Student[3];
//用new为每一个对象数组元素分配存储空间
st1[0]=new Student("li",'F',89);
st1[1]=new Student("he",'M',90);
st1[2]=new Student("zhang",'M',78);
len=3;

//对象数组元素的引用
for(int i=0;i<len;i++) st1[i].studPrint( );
mname=st1[1].getName( );
msex=st1[1].getSex( );
System.out.println("Name 1:"+mname+"/tSex:"+msex);
}
}

运行结果:
Name: li Sex: F Score: 89.0
Name: he Sex: MScore: 90.0
Name: zhang Sex: MScore: 78.0
Name 1:he Sex:M

6.3




图6.7 矩阵
    


    



10 38 56
28 66 90
25 53 89
A

图6.8 二维数组

这里要注意的是:Java中只有一维数组,却不存在称为“二
维数组”的明确结构。然而对一个一维数组而言,其数组元素可
以是数组,这就是概念上的二维数组在Java中的实现方法。也
就是说,在Java语言中,把二维数组实际上看成是其每个数组
元素是一个一维数组的一维数组。其实,这里面的最根本原因
是计算机存储器的编址是一维的,即存储单元的编号从0开始一
直连续编到最后一个最大的编号。因此,如果把图6.7所示的矩
阵用6.8所示的二维数组表示,则在计算机中的存放形式如图6.9
所示。在图6.9中把每一行看成一个数组元素,则有三行的数组
可看成只有三个数组元素,只不过这三个元素又是由三个元素
组成的。

图6.9 二维数组在Java中的实现

6.3.1
二维数组的声明
二维数组的声明与一维数组类似,只是需要给出两对方括
号,其格式如下:
类型说明符 数组名[ ][ ];

类型说明符[ ][ ] 数组名;
例如:
int arr[ ][ ];
或 int [ ][ ] arr;
其中:类型说明符可以是Java的基本类型、类或接口;数组名是
用户遵循标识符命名规则给出的一个标识符;两个方括号中前面
的方括号表示行,后面的方括号表示列。

6.3.2
二维数组的初始化
二维数组声明同样也是为数组命名和指定其数据类型的。
它不为数组元素分配内存,只有经初始化后才能为其分配存储
空间。二维数组的初始化也分为直接指定初值和用new操作符
两种方式。
1
.用new
操作符初始化数组
用new操作符来初始化数组有两种方式:

(1) 先声明数组再初始化数组。在数组已经声明以后,可用
下述两种格式中的任意一种来初始化二维数组。
数组名=new 类型说明符[数组长度][ ];

数组名=new 类型说明符[数组长度][ 数组长度];
其中:对数组名、类型说明符和数组长度的要求与一维数组一
致。

例如:
int arra[ ][ ]; //声明二维数组
arra=new int[3][4];//初始化二维数组
上述两条语句声明并创建了一个3行4列的数组arra。也就是
说arra数组有3个元素,而每一个元素又都是长度为4的一维数组。
实际上共有12个元素,共占用12*4=48个字节的连续存储空间。
这里的语句:
arra=new int[3][4];

实际上相当于下述4条语句:
arra=new int[3][ ];创建一个有3个元素的数组,且每个元
素也是一个数组。
arra[0]=new int[4];创建arra[0]元素的数组,它有4个元素。
arra[1]=new int[4];创建arra[1]元素的数组,它有4个元素。
arra[2]=new int[4];创建arra[2]元素的数组,它有4个元素。
也等价于:
arra=new int[3][ ]
for(int i=0;i<3;i++) { arra[i]=new int[4];}

也就是说,在初始化二维数组时也可以只指定数组的行数
而不给出数组的列数,每一行的长度由二维数组引用时决定。
但不能只指定列数而不指定行数。
上述语句的作用如图6.10所示。

图6.10 语句“arra=new int[3][4];”的作用

(2) 在声明数组时初始化数组。格式如下:
类型说明符[ ][ ] 数组名=new 类型说明符[数组长度][ ];

类型说明符 数组名[ ][ ]=new 类型说明符[数组长度][ 数组长
度];
例如:
int[ ][ ] arr=new int[4][ ];
int arr[ ][ ]=new int[4][3];
但是,不指定行数而指定列数是错误的。例如,下面的初始
化是错误的。
int[ ][ ] arr=new int[ ][4];

2
.直接指定初值的方式
在数组声明时对数据元素赋初值就是用指定的初值对数组
初始化。例如
int[ ][ ] arr1={{3, -9,6},{8,0,1},{11,9,8} };
声明并初始化数组arr1,它有3个元素,每个元素又都是有3个元
素的一维数组。
用指定初值的方式对数组初始化时,各子数组元素的个数
可以不同。例如:
int[ ][ ] arr1={{3, -9},{8,0,1},{10,11,9,8} };

它等价于:
int[ ][ ] arr1=new int[3][ ];
int ar1[0]={3,-9};
int ar1[1]={8,0,1};
int ar1[2]={10,11,9,8};

也等价于:
int[ ][ ] arr1=new int[3][ ];
int arb1[0]={3,-9};
int arb1[1]={8,0,1};
int arb1[2]={10,11,9,8};
arr1[0]=arb1[0]; //实际上是将一个一维数组赋给了一个二维
数组的元素,下同
arr1[1]=arb1[1];
arr1[2]=arb1[2];

6.4
二维数组的引用
6.4.1
测定数组的长度及数组赋值
与一维数组一样,也可以用.length成员方法测定二维数组的
长度,即元素的个数。只不过当使用“数组名.length”的形式测定
的是数组的行数;而使用“数组名[i].length”的形式测定的是该行
的列数。例如,若有如下的初始化语句:
int[ ][ ] arr1={{3, -9},{8,0,1},{10,11,9,8} };
则arr1.length的返回值是3,表示数组arr1有3行或3个元素。
而arr1[2].length的返回值是4,表示arr1[2]的长度为4,即有4个元
素。

【示例程序c6_11.java】 在程序中测定数组的长度。
public class c6_11
{ public static void main(String arg[ ])
{ int i,j;
int len1[ ]=new int[2];
int len2[ ]=new int[2];
int[ ][ ] a1={{1,4,8,9},{3,2,2}};
int a2[ ][ ]={{90,3},{9,12}};
int a1len=a1.length; //将a1数组的长度赋给a1len
int a2len=a2.length; //将a2数组的长度赋给a2len

System.out.println("a1.length="+a1.length);
for(i=0;i<2;i++)
{ len1[i]=a1[i].length; //将a1数组的子数组的长度赋给len1[i]
System.out.println("a1[ ].length="+len1[i]);
}
for(i=0;i<2;i++)
{ for(j=0;j<len1[i];j++)
System.out.print(" "+a1[i][j]);
System.out.println("/n");
}

System.out.println("a2.length="+a2.length);
//赋a2数组的子数组的长度
for(i=0;i<2;i++)
{ len2[i]=a2[i].length;
System.out.println("a2[ ].length="+len2[i]);
}
//打印a2数组的值
for(i=0;i<2;i++)
{ for(j=0;j<len2[i];j++)
System.out.print(" "+a2[i][j]);
System.out.println("/n");
}

a2=a1;//将a1数组赋给a2
System.out.println("a1.length="+a1.length);
//赋a1数组的子数组的长度
for(i=0;i<2;i++)
{ len1[i]=a1[i].length;
System.out.println("a1[ ].length="+len1[i]);
}
//打印a1数组的值
for(i=0;i<2;i++)
{ for(j=0;j<len1[i];j++)
System.out.print(" "+a1[i][j]);
System.out.println("/n");
}

System.out.println("a2.length="+a2.length);
//赋a2数组的子数组的长度
for(i=0;i<2;i++)
{ len2[i]=a2[i].length;
System.out.println("a2[ ].length="+len2[i]);
}
//打印a2数组的值
for(i=0;i<2;i++)
{ for(j=0;j<len2[i];j++)
System.out.print(" "+a2[i][j]);
System.out.println("/n");
}
System.out.println("/n");
}
}

a1.length=2
a1[ ].length=4
a1[ ].length=3
1 4 8 9
3 2 2
a2.length=2
a2[ ].length=2
a2[ ].length=2
90 3
9 12
a1.length=2
a1[ ].length=4
a1[ ].length=3
1 4 8 9
3 2 2
a2.length=2
a2[ ].length=4
a2[ ].length=3
1 4 8 9
3 2 2

6.4.2
数组名作为成员方法的参数
与一维数组类似,二维数组的数组名也可以作为参数传递给
成员方法。下面通过两个例题说明这个问题。
【示例程序c6_12.java】 编程实现在矩阵(用二维数组表示)
中找最大数。
class Maxvalue
{ int maxvl(int arr1[ ][ ])
{
int i,k,max;
int len=arr1.length,len1;

max=arr1[0][0];
for(i=0;i<=len-1;i++)
{ len1=arr1[i].length;
for(k=0;k<len1;k++)
if( arr1[i][k]>max) max=arr1[i][k];
}
return max;
}
}
public class c6_12
{

public static void main(String[ ] args)
{ int maxx;
int arr[ ][ ]={{1,3,7,6},{78,0,42,5},{-98,7,10,-1}};
Maxvalue p1=new Maxvalue( );
maxx=p1.maxvl(arr);
System.out.println("max="+maxx);
}
}
运行结果:
max=78

【示例程序c6_13.java】 两个矩阵相加。
class AddClass
{ void add(int arA[ ][ ],int arB[ ][ ],int arC[ ][ ])
{ int i,k,len1;
int len=arA.length;
for(i=0;i<len;i++)
{ len1=arA[i].length;
for(k=0;k<len1;k++)
arC[i][k]=arA[i][k]+arB[i][k];
}
}
}

public class c6_13
{
public static void main(String[ ] args)
{ int i,k;
int arA[ ][ ]={{1,3,7,6},{78,0,42,5},{-98,7,10,-1}};
int arB[ ][ ]={{1,3,7,6},{78,0,42,5},{-98,7,10,-1}};
int arC[ ][ ]=new int[3][4];
int len=arA.length,len1;
AddClass p1=new AddClass( );
p1.add(arA,arB,arC);
System.out.println("/tA/t/tB/t/tC");
for(i=0;i<len;i++)

{ len1=arA[i].length;
for(k=0;k<len1;k++)
System.out.print(" "+arA[i][k]);//打印第i行A矩阵
System.out.print("/t");
for(k=0;k<len1;k++)
System.out.print(" "+arB[i][k]);//打印第i行B矩阵
System.out.print("/t");
for(k=0;k<len1;k++)
System.out.print(" "+arC[i][k]);//打印第i行C矩阵
System.out.println("/n");
}
}
}

运行结果:
A B C
1 3 7 6 1 3 7 6 2 6 14 12
78 0 42 5 78 0 42 5 156 0 84 10
-98 7 10 -1 -98 7 10 -1 -196 14 20 -2

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页