都知道,对于程序员来说,数组是个好东西。它能帮助我们大幅度提高编程及数据处理的效率。但是,定义和使用数组又有许多规矩,没有彻底厘清之前,总是会一不小心就会制造出一个bug来影响心情。那么,能否一次性帮我把数组讲明白呢?
当然可以,还是以C#语言为例,其他语言关于数组的定义和使用也是相同或者相似的。
关于数组,微软官方是这样定义的:
如何理解这三句话?
第一句话,告诉你数组是一种数据结构,并且构成这种数据结构的变量可以通过索引来访问。这个意思其实就是告诉你,它是一个序列类型的数据。所有序列类型的数据都是可以通过索引访问的,并且索引一定是从0开始,按照递增1的长度来约定索引增量的,所以,其最大索引一定等于序列长度减1。
第二句话,数组中的变量,被称为数组的元素。这只是一个定义,没有别的意思,知道就好。
第三句话,讲了数组的类型问题。这里面其实包含了两层含义:一层是这句话本身明面上的含义,就是明确规定数组元素的数据类型必须一致。也就是不允许多种不同类型的数据对象置于同一个数组之中。这是关于数组元素的一个非常重要的约定,为此,还专门提出了一个概念,也就是构成同一个数组的元素的数据类型,称之为数组的元素类型。除此之外,第三句话的描述还隐藏了另一层含义,那就是数组本身就是一种数据类型,但是,不要误将元素类型等同于数组类型。比如由1,2,3三个整数构成一个数组,我们说它是一个int类型的数组,这里的int类型就是指数组的元素类型,不是数组的类型,因为数组本身就是一种类型,只有它的元素类型会有差别。
综上所述,简而言之,数组就是指同一类数据的一个序列。
好了,理解了数组的概念以后,还有两件事情,我们需要了解和掌握。
第一、数组是引用类型。即使数组的元素类型是值类型,数组依然是引用类型。这个如果能理解上面的第三句话,应该就能很好的理解这件事情。因为两者本来就不相同,而且,作为复合型的数据类型,一般都是引用类型也就不难理解了。这里我说的复合型的数据类型通常是指由一些基本的值类型联合构成的新的数据类型。例如字符串、数组、列表等等。
第二,数组既然是一个序列,它不仅有索引,还一定有长度。但是,不仅如此,它还是定长的。这个是什么意思?还是两层意思,其一,数组在定义的时候,就必须给出它的长度。其二,这个给定的长度在数组的生命周期里不能改变。
好了,明白了上面两点,我们对于数组的概念就应该比较清楚了。接下来,我们介绍一下有关数组的操作,包括数组的创建和赋值。
如何创建一个数组?
请特别注意:创建数组,必须同时指定数组的长度,且该长度不能修改。
int[] a = new int[10]
// 名称:a
// 类型:int
// 长度:3
// 索引:0,1,2。数组元素的索引介于 0 到 Length - 1 之间。
// 默认值:null。new运算符自动将数组默认值初始化为null
如何给数组元素赋值?
以下示例创建一个int[]的同时,直接定义了数组的元素。
int[] a = new int[] {1, 2, 3};
//请注意,此时无需声明数组的长度,因为 ={}里的元素个数
int[] a = new int[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;
//前面两个示例等同于以下示例,更精简,也不用重复声明数组的类型。
int[] a = {1, 2, 3};
对于长度较大,且元素有一定规律的数组,如何赋值?且看以下示例。为了帮助初学者理解代码,我们添加了逐行注释。
//引用命名空间
using System;
//创建一个类
class ArrayExample
{
//创建一个静态无返回值的方法
static void Main()
{
//定义一个int类型的数组a,长度为10
int[] a = new int[10];
//遍历数组
for (int i = 0; i < a.Length; i++)
{
//为每一个元素赋值
a[i] = i * i;
}
//遍历数组
for (int i = 0; i < a.Length; i++)
{
//在控制台显示每一个元素的值
Console.WriteLine($"a[{i}] = {a[i]}");
}
}
}
以上示例创建一个int类型的数组,且该数组的元素由0-9的平方数组成。
到此为止,我们应该已经明白数组的概念、创建和赋值操作了。当然仅有这样简单的案例,还不足以体会到数组的好处和使用效率。但是,也许我们可以隐约的感觉到数组这种数据结构的一些不便之处。
说实话,数组确实存在这样的问题,主要体现在两个方面:
其一,数组这种数据序列,仅支持想同类型的数据集合在一起,对于不同类型的数据在同一个数组结构内,不能兼容。但是,实际场景中,有时难免遇到不一致的数据需要作为一个序列来处理,该怎么办?
其二,数组的长度,不仅要在创建的时候指定,还要维持在它的整个生命周期里不能改变。这样的约定,也可能给我们处理数据带来困惑。比如,从数据库里读取数据存入一个数组,在读取之前我们需要创建一个数组备用,这时,我们并不知道数据记录的条数,也就是数组应有的长度,该怎么处理呢?有经验的程序员当然会有很多解决方案,但是,这难道不是一个问题吗?
基于上面这两点,微软当然是知道的,但是,没有一种数据结构是完美的,它总是只能适应一类数据的定义和存储。好在数据结构总会是多样性的,一定还有超越这个局限的其他数据结构来帮助我们解决世间的各种难题,这个话题留待今后分说。
下面呢,我们干脆把话题扩展一下,介绍两种复杂类型的数组结构。
多维数组
以下示例分别创建了一维、二维、三维数组:
//一维数组,长度10,元素总数10
int[] a1 = new int[10];
//二维数组,长度分别是10、5,元素总数是10*5 = 50
int[,] a2 = new int[10, 5];
//三维数组,长度分别是10、5、2,元素总数是10*5*2 = 100
int[,,] a3 = new int[10, 5, 2];
数组类型的数组
数组的元素类型可以是任意类型,也包括数组类型。包含数组类型元素的数组有时称为交错数组,因为元素数组的长度不必全都一样,但必须注意,一定要是同一类型的数组。
示例:创建一个由 int[] 构成的数组:
int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];
以上代码等效于:
int[][] a = new int[][]{int[10],int[5],int[20]};
int[][] a = {int[10],int[5],int[20]};
OK,当我们了解了数组类型的数组以后,我们的脑海里是否会闪现一个概念,那就是数组的嵌套?没错,正如上面我们看到的,数组是可以嵌套使用的,只需要按照数组的基本定义规则使用即可,一是定长,二是元素同类型即可。
好吧,一发不可收拾,那就继续介绍几个常用的数组操作函数吧。
数组的操作函数
GetValue(Int32),获取一维数组中指定位置的值。索引由一个 32 位整数指定。
SetValue(Object, Int32),给一维数组中指定位置的元素设置值。索引由一个 32 位整数指定。
IndexOf(Array, Object),搜索指定的对象,返回整个一维数组中第一次出现的索引。
Reverse(Array),逆转整个一维数组中元素的顺序。
Sort(Array),使用数组的每个元素的 IComparable 实现来排序整个一维数组中的元素。
好了,今天就到这里吧。这样算不算一次性把数组给你讲明白了呢?