0%

Java专题:数组

概述及定义

在Java语言中,数组是动态创建的对象。

数组的元素:数组对象所包含的变量。

数组的元素类型(component type):数组所有元素都具有的相同的类型。

源数组的成员类型(element type):数组元素类型也可以是数组类型,递归考察数组元素,最终能找到不是数组类型的元素, 该元素的类型就叫作“源数组成员类型”,并且在这个数据结构级别上的元素就称为“源数组的成员”。

数组声明

Java支持两种数组声明的语法:

1
2
type[] identifier;
type identifier[];

第一种声明方式是把[]作为类型的一部分,第二种则作为变量声明符的一部分。第二种语法继承自C,不推荐使用。

不过,这两种声明方式是可以混合使用的,这种“混合标记法”很特别但不太常见,比如:

1
2
byte[] b[]; // 等价于 byte[][] b;
byte[] b1, b2, b3[];

在声明单个数组变量时,“混合标记法”看起来很奇怪,所以通常要么用上述第一种声明语法,要么用第二种; 但是在一个声明语句中声明多个源数组成员类型相同的数组变量时,“混合标记法”是相当简洁的。

无论什么情况下,我们都应该尽量使用第一种声明语法,它使得数组类型更为清晰直观,可读性较强。 第二种声明语法或说“混合标记法”从可读性上来说都是比较差的,而且在一条声明语句中声明多个变量也是不推荐的。

数组创建及初始化

数组可以在声明的同时创建。

数组的创建有两种方式:数组创建表达式或数组初始化器。

数组创建表达式

语法如下:

1
type[] array = new type[length];

比如:

1
double[] ds = new double[10];

使用数组创建表达式创建数组时,只指定了数组长度,而没有指定数组元素的初始值,系统将为每个元素赋默认初始值, 这称为“动态初始化”。设置默认初始值的操作是通过将数组元素对应的内存“清零”而达成的。

数组初始化器

语法如下:

1
type[] array = {e1, ...};

比如:

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

数组初始化器是用花括号括起来并用逗号分隔的表达式列表。最后一个表达式后的逗号是可选的(有则会被忽略), 这一特性使得维护长列表变得更容易。

使用数组初始化器创建数组时,指定的是所有数组元素的初始值,系统将根据表达式列表分配相应长度的空间,由于数组元素初始值是指定的, 所以也叫“静态初始化”。

需要注意的是,数组初始化器是一个数组常量,而数组常量只能在初始化操作中使用。因此,如果不是声明赋值就不能使用数组初始化器, 否则是一个编译错误。如果只是一般的为数组变量赋值,应该使用以下语法:

1
array = new type[]{e1, ...};

数组的嵌套

数组的元素也可以是数组,这就形成了嵌套,数组声明中方括号对数量表示数组嵌套的深度,每一对方括号对应一个维度。

Java中的数组均属于嵌套数组,而常提及的“多维数组”,严格来说,在Java中是没有对应概念的,但是C#中有多维数组。 因此,对比Java和C#中相应的数组概念,将有助于我们理解数组的嵌套和多维。

C#中多维数组指数组有多个维度,比如:

1
int[,] array = new int[4, 2]; // C#

声明创建了一个二维数组,是4行2列的。严格来说,Java中没有与之对应的多维数组的概念与语法。

C#中还有一种数组叫作“交错数组”。交错数组是元素为数组的数组。交错数组元素的维度和大小可以不同。交错数组有时称为“数组的数组”。

C#中交错数组的概念跟Java中的嵌套数组的概念就很类似了,而且语法也几乎一样。

说回嵌套数组,虽然Java没有严格意义上的多维数组,但是,如果把嵌套数组每个级别的元素的维度大小设置为一样的,功能就与多维数组相似了。

在Java语言中,通俗意义上的“多维数组”概念上指的其实是每个级别的子数组维度都相同的嵌套数组,通常说的是“矩形数组”的概念。

而嵌套数组最大的特点是,数组元素的维度和大小可以不同,这种与“矩形数组”相区别的嵌套数组通常称为“参差数组”。

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

数组的复制

嵌套数组的clone是浅复制,即它只创建单个新数组,子数组是共享的。

通常更多的是数组数据的复制,System类中提供了复制数组数据的方法:

1
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

此外,在java.util.Arrays类中提供了更多数组操作的方法,当然也包括复制数据的方法。

数组类型

尽管数组类型不是类,但是每一个数组的Class对象起到的作用都像是如此:直接超类是Object;实现Cloneablejava.io.Serializable接口。

但是,值得注意的是,数组类型的超类型关系与超类关系不同。Integer[]的直接超类型是Number[],但直接超类是Object