Array.push与Array.unshift的性能

时间:2017-05-17 17:41:30

标签: javascript arrays performance time-complexity

我正在阅读关于数组操作的运行时复杂性并了解到......

  • ECMAScript规范没有规定特定的运行时复杂性,因此它取决于具体的实现/ JavaScript引擎/运行时行为[1] [2]
  • 对于由数据结构{{3}等哈希表实现的稀疏数组,
  • Array.push()常量Array.unshift() 线性时间运行}。

现在我想知道pushunshift[3]上是否具有相同的常数线性时间复杂度。 Firefox / Spidermonkey中的实验结果证实:

dense arrays

现在我的问题:

  • 是否有官方文档或参考资料确认观察到的Firefox / Spidermonkey和Chrome / Node / V8的运行时性能?
  • 为什么unshift没有使用类似于push常量运行时实现(例如维护索引偏移量;类似于Array push vs. unshift performance)?

1 个答案:

答案 0 :(得分:8)

要理解这一点,需要了解堆栈(在JavaScript,数组中)如何在计算机科学中设计并在RAM /内存中表示。如果你创建一个堆栈(一个数组),基本上你告诉系统在内存中为一个最终可以增长的堆栈分配一个空间。

现在,每次添加到该堆栈(使用push)时,它都会添加到该堆栈的 end 。最终系统看到Stack不够大,所以它在oldstack.length * 1.5-1中在内存中分配一个新空间,并将旧信息复制到新空间。这就是图表中跳跃/抖动的原因,否则看起来是扁平/线性的。此行为也是您始终应使用var a=new Array(1000)初始化具有预定义大小(如果您知道)的数组/堆栈的原因,以便系统不需要“重新分配内存并复制”。 / p>

考虑unshift,它似乎与推送非常相似。它只是将它添加到列表的开始,对吧?但是这种差异似乎不屑一顾,它非常大!正如推文所解释的那样,当大小耗尽时,最终会有“分配内存并复制”。使用unshift,它想要添加一些内容到开始。但那里已经有了一些东西。所以它必须将位置 N 移动到位置 N + 1 N1到N1 + 1 N2到N2 +1 等因为效率非常低,它实际上只是新分配内存,添加新元素然后通过oldstack复制到newstack。这就是你的图形具有更多二次或甚至略微指数外观的原因。

总结;

  • push添加到结束,很少需要重新分配内存+副本。

  • unshift添加开始始终需要重新分配内存并通过

  • 复制数据

/ edit:关于你的问题为什么用“移动索引”解决这个问题是当你使用unshift并且可以互换推送时,你需要多个“移动索引”和密集计算来找出这个元素的位置索引2实际上驻留在内存中。但Stack背后的想法是O(1)复杂性。

还有许多其他数据结构具有此类属性(以及更多功能),但需要权衡速度,内存使用情况等。其中一些数据结构为VectorDouble-Linked-List,{{1或者甚至是SkipList,具体取决于您的要求

Here is a good resource explaining datastructures and some differences/advancements between them