03_Java数组与方法_01

若想要存放一连串相同数据类型的数据,使用数组是个相当好用的选择。此外,如果某个程序片段经常反复出现,那么将它定义成一个方法可以有效地简化程序代码。本章主要是介绍数组的基本用法与方法的应用,学完本章,将会对数组与方法的使用有更深一层的认识。

数组是由一组相同类型的变量所组成的数据类型,它属于引用类型。它们以一个共同的名称表示,数组中的每个元素则以下标来访问。数组依照存放元素的复杂程度分为一维数组、二维和多维数组,先来看看一维数组。

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]+",\t");
    System.out.println("\n数组长度是:"+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]+",\t");
    System.out.println("\n数组长度是:"+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 + "\t");

System.out.println("\n数组长度是:" + 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("\n数组的最大值是:"+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(
"\n数组拷贝完成!");   } }

 

输出结果:

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("\n数组排序后的顺序:");
    
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("\n总成绩是"+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("\n总成绩是"+sum+" 分!");

 

 

感谢阅读。如果感觉此章对您有帮助,却又不想白瞟

                                 

 

 

posted @ 2018-04-27 10:21  SpringL  阅读(157)  评论(0编辑  收藏  举报