ECMA规范没有指定边界复杂度,但可以从规范的算法中推导出一个。
push
是O(1),但在实践中,它会在引擎定义的边界处遇到O(N)的复制成本,因为需要重新分配插槽数组。这些边界通常是对数的。
pop
是O(1),与push
类似,但O(N)的复制很少遇到,因为它经常被折叠到垃圾收集中(例如,复制收集器只能复制数组的使用部分)。
shift
最坏情况下是O(N),但在特殊情况下,可以实现为O(1),但会降低索引速度,所以具体情况可能有所不同。
slice
是O(N),其中N是end - start
。在不显著减慢两个数组的写入的情况下,这里没有太多优化机会。
splice
最坏情况下是O(N)。有一些数组存储技术可以将N除以一个常数,但它们会显著降低索引速度。如果引擎使用这种技术,当它在访问模式变化时切换存储技术时,您可能会注意到异常缓慢的操作。
你没有提到的一个是sort
。平均情况下,它是O(N log N)。然而,根据引擎选择的算法,在某些情况下,你可能会得到O(N^2)。例如,如果引擎使用QuickSort(即使晚期开启InsertionSort),它具有众所周知的N^2情况。这可能是应用程序DoS的一个来源。如果这是一个问题,请限制您要排序的数组大小(也许合并子数组),或者停止使用HeapSort。
用简单易懂的话来说
push -> O(1)(常数时间复杂度)
pop -> O(1)(常数时间复杂度)
shift -> O(N)(线性时间复杂度)
slice -> O(N)(线性时间复杂度)
splice -> O(N)(线性时间复杂度)
这里有一个关于JavaScript数组时间复杂度的完整解释。
Array.prototype.splice
是一种通用的多功能工具,其复杂性取决于它所执行的操作。例如,它可以删除和/或添加最后一个元素(如 push 或 pop),那么复杂度将为 O(1);如果它执行与 shift / unshift 相同的操作,则其复杂度将为 O(n)。此外,还有其他“中间”情况是可能的。 - Roman Karagodin顺便提一下,使用环形缓冲区(即CircularQueue / CircularBuffer)数据结构可以实现shift
/ unshift
、push
/ pop
方法的O(1)实现。只有在不需要扩展循环缓冲区时,最坏情况下的时间复杂度是O(1)。是否有人真正测量了这些操作的性能?毕竟知道总比猜测好...
如果取的切片数量是未知的,那么切片就是线性的。但如果切片数量是固定的,那么切片就是常数,而不是线性的。
filter
和reverse
,它们是 _O(N)_。 - chuckjconcat
是什么意思? - Elias Zamariaunshift()
,我认为它的时间复杂度也是 O(n)。 - Alexander Mills