3.5 数组
在用程序设计解决实际问题的过程中,往往要处理大量相同类型的数据,而且这些数据要被反复引用,这时候,使用数组这一工具便是一种明智的选择,数组可以使数据有效地排列并且让使用者方便地访问。
通俗一点地说:使用数组的最大好处是:可以让一批相同性质的数据共用一个变量名,而不必为每个数据取一个名字,不仅程序书写大为简便清晰,可读性大大提高,而且便于用重复语句简单处理这类数据,例如,可以用数组变量sin x来记录0~2π之间每隔平均2/π长度的正弦值,用score来表示一个学生所学过的30门课程中每门课程的分数,用height来表示100个小学生中每一个学生的身体高度等等。可以想象,如果使用前面所学的采用一般变量来处理100个小学生的身高问题,比如计算其平均身高、统计身材高于(或低于)平均身高的人数等等,每计算一个问题,都要将100个变量逐个使用(书写)一次,工作量是相当惊人的。
对于处理小学生身高问题,当然不能简单地用height来同时表示100个不等的数,我们还必须把100个数按一定规则组织起来,最简便的方法,是把它们按照某种选定的序号(如1,2,……,100)排列起来,这就是构造数组的最基本手段。这样我们就可以通过变量名字与每一个学生身高值的序号来访问这100个数中的每一个数了。通常我们称这里的序号为下标,并把它们写在方括号中,放在变量名之后。如height[1]表示第一个学生的身高值,height[2]表示第二个学生的身高值,……,height[100]表示第一百个学生的身高值,这样便构成了一个数组,其数组名为height;height[1],height[2],……,height[100]称为该数组的元素。数组依存放元素的复杂程度,分为一维数组、二维数组以及多维(三维以上)数组。首先介绍一维数组。

3.5.1 一维数组的声明与引用
Java中的数组必须先声明,然后才能使用,一维数组声明语法格式如下:   
type<数组名>[]=new type[<元素个数>]
当按上述的语法声明格式声明数组后,系统会分配一块连续的内存空间,供该数组使用。例如:
int myarray[]=new int [10];
表示声明了一个一维整型数组myarray,系统为此配置一块内存空间供该数组使用,其元素个数为10。要使用数组里的元素,可以通过利用其下标来达到目的,只有一个下标值的数组就称为一维数组。Java数组的下标编号从0开始,以上述myarray数组为例,myarray [0]代表该数组第1个元素,myarray[1]代表该数组第2个元素,以此类推,myarray[9]代表该数组第10个元素。因此要注意的是:如果声明的数组元素个数为n,则数组元素下标的变化只能是0~n-1。对于myarray数组来说,如果在程序中出现了对myarray[10]的访问,则会引起下标越界的错误。
例3.5.1 数组的声明与输出:
程序如下:

我们经常使用循环的方式来实现数组的输出。在使用数组过程中,经常要用到数组的元
素个数这一数值,数组的元素个数又被称为该数组的长度。
在程序中可以使用   数组名.length的语句表达方式来获得数组的长度值。

3.5.2 数组的赋值
对数组的赋值有如下方法:
1.在声明时直接赋值,语法格式为:
type<数组名>[]={<数值1>,<数值2>,  ,<数值n>};
在大括号内的数值依次赋值给数组中的第1~n个元素。另外,在赋值声明的时候,不需要给出数组的长度,编译器会视所给的数值个数来决定数组的长度,例如:   int mm[]={2,4,6,8,10,12,14,16,18,20}
在上面的语句中,声明了一个数组mm,虽然没有特别指名mm的长度,但由于括号里的数值有10个,编译器会分别依序指定给各元素存放,使mm[0]=2,mm[1]=4,……mm [9]=20。
2.若是对数组的元素进行有规律的赋值,则可以使用循环的方式进行。例3.5.2 数组的赋值示例。
程序如下:
 

3.5.3 一维数组程序举例
例3.5.3 字符型数组的赋值与输出。
程序如下:

例3.5.4 求出一维数组中的最大值和最小值。
程序如下:


例3.5.5 利用一维数组输出8行杨辉三角形(图3-11)。杨辉三角形:三角形中各行中的数字正好是二项式a+b乘方后,展开式中各项的系数。比如:a+b≠0时
(a+b) 0 =1
(a+b) 1 =a+b
(a+b) 2 =a 2 +2ab+b 2
(a+b) 3 =a 3 +3a 2 b+3ab 2 +b 3 …………
图3ˉ11 杨辉三角形 

仔细观察此三角形,你还可以发现它这样的排列规律:每下一行的数比上一行多一个,两边都是1,中间各数都写在上一行两数的中间,且等于它们的和。为简单起见,本程序忽略输出格式,将每一行的数组元素均放在第一列输出。程序如下:


例3.5.6 用选择法对十个数按从小到大进行排序,然后输出。
选择法的思想方法是:首先在给定的数组中找到一个最小的数,将其换到数组的第一个元素的位置。然后再在第二个数到最后一个数之间求得最小数,将其换到数组的第二个元素的位置。以此类推,直到最后,得到的结果便是已完成的递增序列。
选择法排序程序如下:  

3.5.4 二维数组的声明及引用
在Java中,因为数组元素可以声明成任何类型,因此如果一维数组的元素的数据类型还是一维数组的话,这种数组就被称为二维数组。二维数组声明语法格式如下:  
type<数组名>[][]=new type[<行元素个数>][<列元素个数>]; 
例如:
int My2array[][]=new int[5][6];
上述语句声明了一个二维数组,其中[5]表示该数组有(0~4)5行,每一行有(0~5)6个元素,因此整个数组有30个元素。
对于二维数组元素的赋值,同样可以在声明的时候进行。
例如:  
int ssa[][]={{20,25,26,22},{23,24,20,28}};
声明了一个整型的2行4列的数组,同时进行赋值,结果如下:  
ssa[0][0]=20;
ssa[0][1]=25;
ssa[0][2]=26;
ssa[0][3]=22;
ssa[1][0]=23;
ssa[1][1]=24;
ssa[1][2]=20;
ssa[1][3]=28; 
例3.5.7 二维数组的建立与输出。
程序如下:

Java的多维数组的声明使用相当灵活,它可以从最高维起分别为每一维分配内存,对于创建二维数组来说,可以使用如下的更灵活的声明方式:  

该程序段说明创建的数组第一维长度是arrNum1,第二维长度是arrNum2,如果第二维的大小处处一致,我们可以理解为目前创建的是一个矩阵数组。另外,在Java中还可以创建非矩阵数组。
例如:


arrN数组为5行,每行的元素个数分别为:1、3、5、5、5,甚至可以各不相同。
它产生的二维数组的形式是: 

这也就意味着在Java中可以随时动态地建立数组。
例3.5.8 使用动态建立二维数组的方式输出8行杨辉三角形。
程序如下:


该程序运行结果与例3.5.5相同。

3.5.5 数组的复制
Java在System类中提供了一个特殊的方法arraycopy(),用于实现数组之间的复制,我们通过具体实例来说明该方法的使用。
例3.5.9 数组的复制:使用arraycopy()方法。
程序如下:

3.5.6 字符串处理
SDK1.4.2是采用Unicode V3.0来处理字符的,字符串是内存中一个或多个连续排列的字符集合。Java提供的标准包java.lang中封装的String类就是关于字符串处理的类。这个类封装了很多方法,用来支持字符串的操作。
1.字符串声明及初始化
与其他基本数据类型相似,Java中的字符串分常量和变量两种。当程序中出现了字符串常量,系统将自动为其创建一个String对象,这个创建过程是隐含的。对于字符串变量,在使用之前同样要进行声明,并进行初始化,字符串声明语法格式如下:   String<字符串变量名>;
字符串一般在声明时可以直接进行初始化,初始化过程一般为下面几种:
创建空的字符串:
String s1=newString(); 
由字符数组创建字符串:
char ch[]={′s′,′t′,′o′,′r′,′y′};String s2=newString(ch);
直接用字符串常量来初始化字符串:  
String s3=″Hel lo!Welcome to Java!″;
 2.字符串运算符“+”
在Java中,运算符“+”除了作为算术运算符使用之外,它还经常被作为字符串运算符用于连接不同的字符串。即它的运算规则是:“abc”+“def”=“abcdef”。但如果在你的运算表达式中还有其他类型的数据,我们就有必要注意一下“+”的工作方式了。首先请看下面的例题。
例3.5.10 “+”的运算方式:  


从上面的程序运行结果可以看到:如果“+”的两端都是字符串的话,则“+”的功能是将这两个字符串连接起来;如果在“+”两端有一个是字符串,Java就会将非字符串的数据转换为字符串,然后进行字符串连接运算;而当“+”的两端是数值类型数据时,Java则将“+”号作为算术运算符进行加运算。希望大家在以后进行的程序设计过程中注意这些区别。
3.String类
String类中常用的方法有:
1)int length():返回当前字符串中的字符个数。
2)boolean equals(String str):区分大小写比较两个字符串的内容是否相等。
3)boolean equalsIgnoreCase(String str):不区分大小写比较两个字符串的内容是否相等。
4)char charAt(int index):返回字符串中index处位置的字符。
5)String toLowerCase():将当前字符串中所有字符转换为小写形式。
6)String toUpperCase():将当前字符串中所有字符转换为大写形式。
7)String substring(int BIndex):截取当前字符串中从BIndex开始到末尾的子串。
8)boolean startsWith(String str):测试当前字符串是否以str字符串为开头。
9)char replace(char c1,char c2):将当前字符串中的c1字符转换为c2字符。
10)String trim():返回去掉了当前字符串前后空格的字符串。
11)int indexOf(String str,int i):在当前的字符串中从i处查找str子串,若找到,返回子串第一次出现的位置,否则返回-1。
使用String类方法的基本形式:
<字符串变量名>.<方法名>(<参数>);
式中的<字符串变量名>就是当前要处理的字符串。
例3.5.11 字符串比较方法的使用。
程序如下:


本例题要提请大家注意的问题是:String并非Java的基本数据类型,而是java.lang中的(字符串)类,s1和s2是String类中的两个对象(关于类和对象的基本概念将在第4章中向大家作详细介绍),因此要比较s1和s2中的内容是否相同,要使用equals()或者equalsIgnoreˉCase()方法,而不是用“==”去进行判别。例3.5.12 String类常用方法的使用。
程序如下:
 
例3.5.13 使用字符串处理方法对文本语句中的单词记数。
程序如下:


看了上面的例题,各位有什么问题吗?一定有的。首先“Character”是什么?如果说是对象或是变量,则它并没有被声明过,显然它也不会是Java的运算符,其实,Character与String一样也是java.lang中的类,但它是字符类。在本例题中的toLowerCase()方法是封装于Character类中的,它的作用是返回括号内字符的小写字母的Unicode代码值(Unicode前128位代码值与ASCII代码值相同)。
接下来的问题是:为什么Character类中的方法可以通过类名直接调用,而String类中的方法则必须通过声明字符串变量以后由字符串变量名来调用呢?那是因为:Character类中封装的toLowerCase()方法,是静态(static)的。Java规定:凡静态的方法被调用时无须创建它所在类的对象,而可以以该类的类名直接调用。而String类中封装的toLowerCase()方法则是非静态的,因而必须通过创建对象以后由对象名来调用。以上所讲的这些内容可以参照
第4章的相关章节。
4.StringBuffer类
Java的String类提供了字符串查找及测试的一些方法,但如果要对字符串做连接或修改等操作,则必须使StringBuffer类来声明字符串,并用这个类所提供的方法来进行操作。StringBuffer类与String类一样,都被置于java.lang类库中。StringBuffer类的常用方法有:
1)StringBuffer append(char c):将字符c放到字符串缓冲区之后。
2)StringBuffer append(String str):将字符串str放到字符串缓冲区之后。
3)StringBuffer deleteCharAt(int index):删除字符串缓冲区中第index位置的字符。
4)StringBuffer insert(int k,char c):在字符串缓冲区的第k个位置插入字符c。
5)StringBuffer insert(int k,String str):在字符串缓冲区的第k个位置插入字符串str。
6)StringBuffer replace(int m,int n,String str):将字符串缓冲区中第m到n之间以字符串str取代。
7)StringBuffer reverse():将字符串缓冲区中的字符串按反向排列。
例3.5.14 StringBuffer类常用方法的使用。


在String类与StringBuffer类中还封装了许多其他的方法,同样希望大家学习与使用时,经常参阅API文档。