数组指的是线性列表,在内存中是一段连续的内存空间,存储的数据类型一致。
两个特点:
1)线性表,线性表就是数据排成像一条线一样的结构,每个线性表的数据最多只有前和后两个方向;
除了数组,链表、队列、栈等也是线性表结构。与它相对立的概念是非线性表,比如二叉树、堆、图等。之所以叫非线性表,是因为,在非线性表中,数据之间并不是简单的前后关系。
2)连续的内存空间和相同的数据类型,所以它可以“随机访问”,但因此它的增删操作都很低效,每次增或删为了保持连续性就需要做大量的数据搬移工作。
随机访问实现:
如图,用一个长度为10的int类型数组,假设内存首地址base_address为1000,计算机分配的内存空间为1000~1039。
计算机会给每一个内存单位分配地址,计算机通过地址来访问内存中的数据,当需要随机访问数组中的某一元素时,它会首先通过寻址公式计算出元素存储的内存地址:
a[i]_address = base_address + i * data_type_size
其中i为元素下标,data_type_size表示数组中元素大小。该数组中元素类型为int,所以data_type_size大小为4个字节。
此处记录一下为什么数组都是从0下标开始?
因为当下标从1开始时,寻址公式就变为:
a[i]_address = base_address + (i-1) * data_type_size
这样每次随机访问数组都多了一次减法运算,cpu就会多出一次减法指令。数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的变成操作,效率优化需要尽可能做到极致。
时间复杂度:数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。
===============================================================
低效的插入和删除操作
插入:当数组长度为n,需要在k的位置插入数据,为了把第k位置空出来,需要将k~n的元素都顺序地向后移动一位。当插入的位置时末位则时间复杂度为O(1),如果是首位或者其他位置,其后的元素将向后移动,最坏时间复杂度为O(n),因为每个位置元素插入的概率都相同,平均时间复杂度为(1+2+···+n)/n = O(n);
删除:同上,若删除第k位元素,为了保持数组内存的连续性,同样需要移动元素。与插入类似,删除末位,是最优时间复杂度O(1),删除第k位元素,最坏时间复杂度位O(n),平均时间复杂度位O(n)。
注:
1)插入操作时,若数组对元素的顺序没有要求(无序),那么可以将要k对应的元素直接移到末位,然后将新元素直接插入k位置。特殊情况下,时间复杂度为O(1)。
2)删除操作时,若对数组中要删除的元素进行记录暂不删除,每次的删除操作并不是真正地搬移数据只是记录数据已经被删除,等到数组没有更多空间存储数据时,再触发执行一次真正的删除操作,这样就大大减少了数据搬移。(JVM标记清除垃圾回收算法)
=================================================================
警惕数组的访问越界问题
用C语言循环越界访问的例子说明访问越界的bug。
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}
这段代码的运行结果并非是打印三行“hello word”,而是会无限打印“hello world”。
当然再java中不会出现这样的问题而是直接抛出java.lang.ArrayIndexOutOfBoundsException异常。
对于c这个情况,内存地分配是栈结构向下增长的,首先压栈的i,a[2],a[1],a[0],当访问到a[3]时,是在访问i变量,此时i变量的地址是数组当前进程的,所以修改时操作系统不会终止进程。
有个例子很形象:在Excel中从上往下拉4个格子,变量i会先被分配到第4个格子的内存,然后变量arr往上数分配3个格子的内存,但arr的数据是从分配3个格子的第一个格子从上往下存储数据的,当访问第3索引时,这时刚好访问到第4个格子变量i的内存。而a[3]=0就相当于i=0,于是陷入无限循环。
================================================================================
用数组还是容器?
数组先指定了空间大小
容器如ArrayList可以动态扩容。
1.希望存储基本类型数据,可以用数组
2.事先知道数据大小,并且操作简单,可以用数组
3.直观表示多维,可以用数组
4.业务开发,使用容器足够,开发框架,追求性能,首先数组。