数组是线性的数据结构,其特点是会将
由于其内存空间的连续,通过下标访问元素的时间复杂度为O(1)。
基本概念
存在一个数组:1, 1, 4, 5, 1, 4
相关的重要概念有:
- 元素:数组的构成单位,要求类型
相同或兼容 (如继承关系),比如1,5。 - 元素大小:即单个元素占用的内存空间。在 .NET 中,
int占用 32 位(4字节),详见整型数值类型。 - 维度:上述例子的数组是一维数组(Rank 为 1)。二维数组的定义方式如
int[,] arr = new int[2, 3],表示一个2行3列的矩阵。 - 维度长度:数组在某一维度上的元素数量(Length),如上述例子的维度长度为6。
- 索引:元素在数组中的位置。在 .NET 中,索引从0开始,比如元素5的索引是3。
.NET 对数组的处理
在 .NET 中,数组本身是System.Array 基类。因此,无论数组元素是值类型还是引用类型,数组对象本身都存储在托管堆上。
需要注意的是,如果数组元素是引用类型(如 string[]),数组内存中存储的是指向对象的引用(指针);如果是值类型(如 int[]),元素的值直接存储在数组的连续内存块中。
因此, .NET 会将数组放在托管堆上,而不是栈。
操作
实例化
一维数组:
1 | int[] arr = new int[5]; // 5个元素的数组 |
多维数组:
1 | int[,] arr = new int[2, 3]; // 2行3列的二维数组 |
访问
数组在内存中是连续存储的,其优势在于可以高效地访问任意元素,时间复杂度为O(1)。
1 | int[] arr = new int[] { 1, 2, 3 }; |
插入 & 删除 & 添加
以插入操作为例:
在内存中连续存储的特性导致数组
然而,如果要让原数组的元素得以保留,那么就需要新建一个更长的数组,然后把原数组的元素复制到新数组中,再插入元素。
这也是基于数组实现列表的基本原理之一。
1 | int[] arr = {1, 1, 5, 1, 4}; |
不难发现,由于在数组中,元素紧密排列,从而导致在最坏情况下,插入元素的时间复杂度为O(n)。
进而可以得知,删除或添加元素的时间复杂度也为O(n)。
总结
得益于数组在内存中的连续存储
- 数组对内存拥有更高效率的利用:元素在数组中的紧密排列意味着数组
几乎不会浪费内存 。 - 极高的访问速度:通过索引访问元素的时间复杂度为
O(1)。
但也因为这个特性
- 新增&插入&删除的效率低:涉及到元素增删的操作会不可避免地导致元素的移动,时间复杂度为
O(n)。 - 固定的长度:如果出现数组长度不够用的情况,需要
扩容 。