《Java从小白到大牛》之第8章 数组

在计算机语言中数组是非常重要的集合类型,大部分计算机语言中数组具有如下三个基本特性:

  1. 一致性:数组只能保存相同数据类型元素,元素的数据类型可以是任何相同的数据类型。
  2. 有序性:数组中的元素是有序的,通过下标访问。
  3. 不可变性:数组一旦初始化,则长度(数组中元素的个数)不可变。

在Java中数组的下标是从零开始的,事实上很多计算机语言的数组下标从零开始的。Java数组下标访问运算符是中括号,如intArray[0],表示访问intArray数组的第一个元素,0是第一个元素的下标。

另外,Java中的数组本身是引用数据类型,它的长度属性是length。数组可以分为:一维数组和多维数组,下面先介绍一维数组。

一维数组

当数组中每个元素都只带有一个下标时,这种数组就是“一维数组”。数组是引用数据类型,引用数据类型在使用之前一定要做两件事情:声明和初始化。

数组声明 {#-0}

数组的声明就宣告这个数组中元素类型,数组的变量名。

注意 数组声明完成后,数组的长度还不能确定,JVM(Java虚拟机)还没有给元素分配内存空间。

数组声明语法如下:

元素数据类型[] 数组变量名;

元素数据类型 数组变量名[];

可见数组的声明有两种形式:一种是两个中括号([])跟在元素数据类型之后;另一种是两个中括号([])跟在变量名之后。

提示 从面向对象角度看,Java更推荐采用第一种声明方式,因为它把“元素数据类型[]”看成是一个整体类型,即数组类型。而第二种是C语言数组声明方式。

数组声明示例如下:

int intArray[];

float[] floatArray;

String strArray[];

Date[] dateArray;

数组初始化 {#-1}

声明完成就要对数组进行初始化,数组初始化的过程就是为数组每一个元素分配内存空间,并为每一个元素提供初始值。初始化之后数组的长度就确定下来不能再变化了。

提示 有些计算机语言虽然提供了可变类型数组,它的长度是可变的,这种数组本质上是创建了一个新的数组对象,并非是原始数组的长度发生了变化。

数组初始化可以分为静态初始化和动态初始化。

  1. 静态初始化

静态初始化就是将数组的元素放到大括号中,元素之间用逗号(,)分隔。示例代码如下:

int[] intArray;

//静态初始化int数组

intArray = {21,32,43,45};

String[] strArray;

//静态初始化Stirng数组

strArray = {"张三","李四","王五","董六"};

//声明同时初始化数组

int intArray[] = {21,32,43,45};

String strArray[] = {"张三","李四","王五","董六"};

静态初始化是在已知数组的每一个元素内容情况下使用的。很多情况下数据是从数据库或网络中获得的,在编程时不知道元素有多少,更不知道元素的内容,此时可采用动态初始化。

  1. 动态初始化

动态初始化使用new运算符分配指定长度的内存空间,语法如下:

new 元素数据类型[数组长度] ;

示例代码如下:

int intArray[];

// 动态初始化int数组

intArray = new int[4]; ①

intArray[0] = 21;

intArray[1] = 32;

intArray[2] = 43;

intArray[3] = 45; ②

// 动态初始化String数组

String[] stringArray = new String[4]; ③

// 初始化数组中元素

stringArray[0] = "张三";

stringArray[1] = "李四";

stringArray[2] = "王五";

stringArray[3] = "董六"; ④

上述代码第①行和第③行通过new运算符分配了4个元素的内存空间。

提示 new分配数组内存空间后,数组中的元素内容是什么呢?答案是数组类型的默认值,不同类型默认值是不同的,如表8-1所示。

表 8-1 数据类型默认值

基本类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
boolean false
引用 null

当代码第①行执行完成,intArray数组内容如图8-1(a)所示,intArray数组中的所有元素都是0,根据需要会动态添加元素内容,代码第②行执行完成,intArray数组内容如图8-1(b)所示。

当代码第③行执行完成,stringArray数组内容如图8-2(a)所示,stringArray数组中所有元素都是null,随着每一个元素被初始化和赋值,代码第④行执行完之后每个元素都有不同内容,这里需要注意的是引用类型数组,每一个元素保存都是指向实际对象的内存地址,如图8-2(b)所示,每个对象还需要创建和初始化过程,有关对象创建和初始化内容,将在后面章节详细介绍。

图8-1 intArray数组

图8-2 stringArray数组

案例:数组合并 {#-2}

数组长度是不可变,要想合并两个不同的数组,不能通过在一个数组的基础上追加另一个数组实现。需要创建一个新的数组,新数组长度是两个数组长度之和。然后再将两个数组的内容导入到新数组中。

下面具体看看实现代码:

public class HelloWorld {

public static void main(String[] args) {

// 两个待合并数组

int array1[] = { 20, 10, 50, 40, 30 };

int array2[] = { 1, 2, 3 };

// 动态初始化数组,设置数组的长度是array1和array2长度之和

int array[] = new int[array1.length + array2.length];

// 循环添加数组内容

for (int i = 0; i < array.length; i++) {

if (i < array1.length) { ①

array[i] = array1[i]; ②

} else {

array[i] = array2[i - array1.length]; ③

}

}

System.out.println("合并后:");

for (int element : array) {

System.out.printf("%d ", element);

}

}

}

上述代码第①行是判断当前循环变量i是否小于array1.length,在此条件下将array1数组内容导入新数组,见代码第②行。当array1数组内容导入完成后,再通过代码第③行将另一个数组array2导入到新数组,其中array2下标应该是i - array1.length。

多维数组

当数组中每个元素又可以带有多个下标时,这种数组就是“多维数组”。本节重点介绍二维数组。

二维数组声明 {#-0}

Java中声明二维数组需要有两个中括号,具体有三种语法如下:

元素数据类型[][] 数组变量名;

元素数据类型 数组变量名[][];

元素数据类型[] 数组变量名[];

三种形式中前两种比较好理解,最后一种形式看起来有些古怪。数组声明示例如下:

int[][] array1;

int array1[][];

int[] array1[];

二维数组的初始化 {#-1}

二维数组的初始化也可以分为静态初始化和动态初始化。

  1. 静态初始化

静态初始化示例如下:

int intArray[][] = { { 1, 2, 3 }, { 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 } };

上述代码创建并初始化了一个4×3二维数组,理解Java中的多维数组应该从数组的数组的角度出发。首先将intArray看成是一个一维数组,它有4个元素,如图8-3所示,其中第1个元素是{ 1, 2, 3 },第2个元素是{ 11, 12, 13 },第3个元素是{ 21, 22, 23 },第4个元素是{ 31, 32, 33 }。然后再分别考虑每一个元素,{ 1, 2, 3 }表示形式说明它是一个int类型的一维数组,其他3个元素也是一维int类型数组。

图8-3 intArray二维数组

提示 严格意义上说Java中并不存在真正意义上的多维数组,只是一维数组,不过数组中的元素也是数组,以此类推三维数组就是数组的数组的数组了,例如{ { {1, 2}, {3} }, { {21}, {22, 23} } }表示一个三维数组。

  1. 动态初始化

动态初始化二维数组语法如下:

new 元素数据类型[高维数组长度] [低维数组长度] ;

高维数组就是最外面的数组,低维数组是每一个元素的数组。动态创建并初始化一个4×3二维数组示例代码如下:

int[][] intArray = new int[4][3];

二维数组的下标[4][3]有两个,前面的[4]是高维数组下标索引,后面的[3]是低维数组下标索引。4×3二维数组的每一个元素的下标索引,如图8-4(a)所示。由于低维数组是int类型,所以初始化完后所有元素全部是0,如图8-4(b)所示。
图8-4 二维数组动态初始化

二维数组示例如下:

public class HelloWorld {

public static void main(String[] args) {

// 静态初始化二维数组

int[][] intArray = {

{ 1, 2, 3 },

{ 11, 12, 13 },

{ 21, 22, 23 },

{ 31, 32, 33 } };

// 动态初始化二维数组

double[][] doubleArray = new double[4][3];

// 计算数组intArray元素的平方根,结果保存到doubleArray

for (int i = 0; i < intArray.length; i++) {

for (int j = 0; j < intArray[i].length; j++) {

// 计算平方根

doubleArray[i][j] = Math.sqrt(intArray[i][j]); ①

}

}

// 打印数组doubleArray

for (int i = 0; i < doubleArray.length; i++) {

for (int j = 0; j < doubleArray[i].length; j++) {

System.out.printf("[%d][%d] = %f", i, j, doubleArray[i][j]);

System.out.print('\t');

}

System.out.println();

}

}

}

代码第①行是中Math.sqrt(intArray[i][j])表达式是计算平方根,Math是java.lang包中提供的用于数学计算类,它提供很多常用的数学计算方法,sqrt是计算平方根,如取绝对值的abs、幂运算的pow等。

不规则数组 {#-2}

由于Java多维数组是数组的数组,因此会衍生出一种不规则数组,规则的4×3二维数组有12个元素,而不规则数组就不一定了。如下代码静态初始化了一个不规则数组。

int intArray[][] = { { 1, 2 }, { 11 }, { 21, 22, 23 }, { 31, 32, 33 } };

高维数组是4个元素,但是低维数组元素个数不同,如图8-5所示,其中第1个数组有两个元素,第2个数组有1个元素,第3个数组有3个元素,第4个数组有3个元素。这就是不规则数组。

图8-5 不规则数组

动态初始化不规则数组比较麻烦,不能使用new int[4][3]语句,而是先初始化高维数组,然后再分别逐个初始化低维数组。代码如下:

int intArray[][] = new int[4][]; //先初始化高维数组为4

//逐一初始化低维数组

intArray[0] = new int[2];

intArray[1] = new int[1];

intArray[2] = new int[3];

intArray[3] = new int[3];

从上述代码初始化数组完成之后,不是有12个元素而是9个元素,它们的下标索引如图8-6所示,可见其中下标[0][2]、[1][1]和[1][2]是不存在的,如果试图访问它们则会抛出下标越界异常。
图8-6 不规则数组访问

提示 下标越界异常(ArrayIndexOutOfBoundsException)是试图访问不存在的下标时引发的。例如一个一维array数组如果有10个元素,那么表达式array[10]就会发生下标越界异常,这是因为数组下标是从0开始的,最后一个元素下标是数组长度减1,所以array[10]访问的元素是不存在的。

下面介绍一个不规则数组的示例:

public class HelloWorld {

public static void main(String[] args) {

int intArray[][] = new int[4][]; //先初始化高维数组为4

//逐一初始化低维数组

intArray[0] = new int[2];

intArray[1] = new int[1];

intArray[2] = new int[3];

intArray[3] = new int[3];

//for循环遍历

for (int i = 0; i < intArray.length; i++) {

for (int j = 0; j < intArray[i].length; j++) {

intArray[i][j] = i + j;

}

}

//for-each循环遍历

for (int[] row : intArray) { ①

for (int column : row) { ②

System.out.print(column);

//在元素之间添加制表符,

System.out.print('\t');

}

//一行元素打印完成后换行

System.out.println();

}

//System.out.println(intArray[0][2]); //发生运行期错误 ③

}

}

不规则数组访问和遍历可以使用for和for-each循环,但要注意下标越界异常发生。上述代码第①行和第②行采用for-each循环遍历不规则数组,其中代码第①行for-each循环取出的数据是int数组,所以row类型是int[]。代码第②行for-each循环取出的数据是int数据,所以column的类型int。

另外,注意代码第③行试图访问intArray[0][2]元素,由于[0][2]不存在所以会发生下标越界异常。

本章小结

本章介绍了Java的数组,包括一维数组和多维数组,读者要重点掌握一维数组的声明、初始化和使用,了解二维数组的声明、初始化和使用。另外,还需要了解不规则数组。

配套视频

http://edu.51cto.com/topic/1507.html

配套源代码

http://www.zhijieketang.com/group/5

与本书免费版对应的还有一个收费版本:

  1. 进入百度阅读电子书

  2. 进入图灵社区电子书
相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
本站公众号
   欢迎关注本站公众号,获取更多程序园信息
开发小院