导读
在大部分编程语言中,数组都是从 0 开始编号的,但你是否下意识地想过,为什么数组要从 0 开始编号,而不是从 1 开始呢? 从 1 开始不是更符合人类的思维习惯吗?
1、什么是数组
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
2、数组的操作实现
2.1、随机访问
数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。
2.2、插入
如果在数组的末尾插入元素,那就不需要移动数据了,这时的时间复杂度为 O(1)。但如果在数组的开头插入元素,那所有的数据都需要依次往后移动一位,所以最坏时间复杂度是 O(n)。 因为我们在每个位置插入元素的概率是一样的,所以平均情况时间复杂度为 (1+2+…n)/n=O(n)。
2.3、删除
如果我们要删除第 k 个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞,内存就不连续了。
和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。
2.4、数组越界
Java 本身就会做越界检查,比如下面这几行 Java 代码,就会抛出 java.lang.ArrayIndexOutOfBoundsException。
int[] a = new int[3];
a[3] = 10;
3、容器能否代替数组
相比于数字,java中的ArrayList封装了数组的很多操作,并支持动态扩容。一旦超过存储容量,扩容时比较耗内存,因为涉及到内存申请和数据搬移。
4、数组适合的场景
- Java ArrayList 的使用涉及装箱拆箱,有一定的性能损耗,如果特别关注性能,可以考虑数组。
- 若数据大小事先已知,并且涉及的数据操作非常简单,可以使用数组。
- 表示多维数组时,数组往往更加直观。
- 业务开发建议使用容器,如果是底层开发,如网络框架,性能优化,那么选择数组。
5、数组为什么从0开始编号
5.1、寻址公式
由于数组是通过寻址公式,计算出该元素存储的内存地址:
a[i]_address = base_address + i * data_type_size
如果数组是从 1 开始计数,那么就会变成:
a[i]_address = base_address + (i-1)* data_type_size
对于CPU来说,多了一次减法的指令。
5.2、历史原因
C 语言设计者用 0 开始计数数组下标,之后的 Java、JavaScript 等高级语言都效仿了 C 语言,或者说,为了在一定程度上减少 C 语言程序员学习 Java 的学习成本,因此继续沿用了从 0 开始计数的习惯。实际上,很多语言中数组也并不是从 0 开始计数的,比如 Matlab。甚至还有一些语言支持负数下标,比如 Python。