JavaScript中的数组
什么是数组?数组是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的的数据。但是在JavaScript数组却不一样,JavaScript中的数组不是以一组连续的内存空间来存储数据的,可以不用指定数组类型,也不用指定具体的类型,可以存储任意类型的数据,带来方便的同时,js数组的性能相比于其他语言的数组要差。
数组是如何实现随机访问的
上面说到数组用一组连续的内存空间来存储一组数据,那么在内存中具体是怎么表现的呢?
以java为例,通过 int a[] = new int[5],在内存中申请了一块连续的存储空间1000~1016,,同时会给每个内存单元分配一个地址,内存的首地址为baseAddress = 1000 类似下图:
计算机通过地址来访问内存中的数据,当需要访问数组中某个元素的数据时,会通过下面的寻址公式得到对应的地址,再去访问。
address = baseAddress + index(索引) * baseSize(对应类型每个内存单元的大小)
比如图中的例子,数组的首地址是1000,数组的每个内存单元的大小是4,要获取a[2]的值,就会通过上面的公式1000 + 2 * 4 计算出a[2]在内存中的地址为1008,从而访问这个地址获取到值。
js中数组竟然还分快慢?
在js中的数组分两种表现形式,一种是快数组和传统的数组差不多的的数组,类似于Java中ArrayList,到达一定的程度,会自动将数据拷贝到更大的内存空间中去,扩容后的新容量=旧容量的1.5倍。而另一种是慢数组,这种数组不是以一组连续的空间来存储的,而是以一种hash映射的方式,它可以通过多钟数据结构实现,其中一种是链表。以链表为例,通过 const a = new Arraay(5)来声明一个长度为5的数组,在内存中的表现形式如下
如图,变量a中存储的就是链表的头结点a0的地址1000,如果要获取a[3]的值,那么就需要将链表从a0开始向后遍历3个节点才能将值取出来,所以慢数组的性能要比传统意义的数组差一点。
关于js数组在内存中的表现问题,推荐大家可以看看这两篇文章
深究 JavaScript 数组 —— 演进&性能
juejin.cn/post/684490…
探究JS V8引擎下的“数组”底层实现
blog.csdn.net/wanderlustL…
为什么会出现快慢两种数组呢?这是为了对js的数组进行性能的优化,快数组申请连续的内存空间,查询速度更快,是以空间换时间;而慢数组不必申请连续的空间,以时间换空间,节省了内存,在达到一定条件时,快慢数组会进行切换。
初始化数组的时候用两种数组的哪一种呢?当不指定数组大小,或数组大小小于1024的时候,数组都是以快数组的形式存放的。当数组的长度大于1024就会采用hash表来存放,也就是慢数组的形式存放。
那么什么时候会进行快慢数组的切换呢?当快数组通过扩容超过1024的阈值就会切换为慢数组,或者当数组中的空元素超过1024,就会切换为慢数组。当慢数组中的数据可以存放在快数组中并且长度在smi(- 2^31 ~ 2^ 31 -1)以内,并且可以节省超过50%的空间就会切换为快数组。
数组中的下标为什么是从0开始的?
一种原因是为了极致的性能,因为如果下标从1开始的话,就会多一次减法运算。
address = baseAddress + index(索引) * baseSize(对应类型每个内存单元的大小)
address = baseAddress + (index - 1) * baseSize(对应类型每个内存单元的大小)
还有一种是历史原因造成的,因为C语言的设计者用0作为数组的下标,之后的Java、JavaScript等高级语言也效仿了C语言把0作为开始的下标。
数组适合查找?查找的复杂度为O(1)?
数组是适合查找的,但是复杂度并不是O(1),因为即便是排好序的数组,用二分法查找时间复杂度也是nlog(n)。那么复杂度O(1)的是什么呢?是数组根据下标随机访问。
数组低效的插入和删除。
数组的插入操作为什么效率比较低呢?看下面这张图。
比如将元素c插入到d元素的前面,我们需要将d以及d后面的元素全部往后挪一位。我们来分析一下往长度为n的数组中的第k个位置插入一个元素x的复杂度,最好情况下,k=n,此时的时间复杂度为O(1),最坏情况,k=0,会把所有的数据往后挪一位,时间复杂度为O(n)。平均复杂为(1+2+...+n)/n= n/2,所以平均复杂度为O(n)。
如果数组中的数据没有顺序要求的话,我们可以令arr[n] = arr[k],arr[k] = x;这样就可以将插入操作的时间复杂度降为O(1)。
数组的删除操作又为什么效率低呢?
和插入的情况类型,为了保证数组数组内存的连续性,也需要搬移数据,不然内存就不连续了。和插入类似,最好情况删除最后一个元素,时间复杂度为O(1),删除第一个元素,时间复杂度为O(n),同理,平均复杂度为也为O(n)。