JavaScript数组的大O


105

JavaScript中的数组很容易通过添加和删除项来进行修改。它在某种程度上掩盖了一个事实,即大多数语言数组都是固定大小的,并且需要复杂的操作来调整大小。看起来,JavaScript使编写性能不佳的数组代码变得容易。这导致了一个问题:

对于数组性能,我可以从JavaScript实现中获得什么样的性能(就O时间复杂度而言)?

我假设所有合理的JavaScript实现最多都具有以下大O。

  • 存取权-O(1)
  • 追加-O(n)
  • 前置-O(n)
  • 插入-O(n)
  • 删除-O(n)
  • 交换-O(1)

JavaScript使您可以使用以下命令将数组预填充为特定大小 new Array(length)语法。(奖金问题:是以O(1)或O(n)的方式创建数组)这更像是常规数组,并且如果用作预大小数组,则可以允许添加O(1)。如果添加了循环缓冲区逻辑,则可以实现O(1)前置。如果使用动态扩展数组,则O(log n)将是这两种情况的平均情况。

我可以期望某些事情比我的假设有更好的性能吗?我不希望任何规范概述任何内容,但实际上,可能是所有主要实现都在幕后使用了优化的数组。是否在工作中动态扩展数组或其他一些提高性能的算法?

聚苯乙烯

我想知道这是因为我正在研究一些排序算法,当描述它们的整体大O时,大多数似乎都假定追加和删除是O(1)运算。


6
在现代JavaScript实现中,具有大小的Array构造函数几乎没有用。在那种单一参数形式下,它几乎什么也不做。(它设置了.length,仅此而已。)数组实际上与普通Object实例没有太大区别。
尖尖的

3
设置length属性和预分配空间是完全不同的两件事。
Pointy's

1
@Pointy:我是期望太高了,当我希望设置array[5]一个new Array(10)是O(1)?
肯德尔·弗雷

1
尽管ECMAScript 没有定义Array对象的实现方式(它仅定义了一些语义规则),但是很可能不同的实现将针对预期的情况进行优化(例如,对于大小小于n的数组具有“真实数组”支持) )。我对实现并不那么精明,但是如果没有在某处完成,我会感到非常惊讶 ……

5
@KendallFrey“最佳答案”可能会针对不同的n /访问模式编写一些jsperf测试用例,并查看结果;-)

Answers:


111

注意:虽然这个答案在2012年是正确的,但当今引擎对对象和数组使用的内部表示形式都非常不同。这个答案可能是正确的,也可能不是。

与大多数在Java语言中使用数组实现数组的语言相反,数组是对象,值存储在哈希表中,就像常规对象值一样。因此:

  • 存取权-O(1)
  • 追加-摊销O(1)(有时需要调整哈希表的大小;通常仅需要插入)
  • 通过前置-O(n)unshift,因为它需要重新分配所有索引
  • 插入-如果值不存在,则摊销O(1)。如果要转移现有值(例如,使用splice),则为O(n )。
  • 删除-分摊O(1)以删除值O(n)(如果要通过分配索引)splice
  • 交换-O(1)

通常,设置或取消设置dict中的任何键都是摊销O(1),对于数组,无论索引是什么,也是如此。需要重新编号现有值的任何操作都是O(n),这仅仅是因为您必须更新所有受影响的值。


4
不应该以O(n)开头吗?由于所有索引都需要移动。对于插入和删除(在任意索引处,以及将元素移位/折叠)相同。
nhahtdh 2012年

2
另外,是length在Array突变上设置的,还是get在它上面获取长度并可能记住它?
alex

27
值得一提的是这个答案不再正确。除非它们是稀疏的,否则现代引擎不会将数组(或具有索引整数键的对象)存储为哈希表(但就像C中的数组一样)。让您开始这里是一个“经典”基准来说明这一点
Benjamin Gruenbaum 2013年

4
这是由标准定义的还是仅仅是JS引擎中的常见实现?什么是V8?
艾伯特

4
@BenjaminGruenbaum,如果您可以对它们的存储方式有所了解,那将是很好的。或提供一些资料。
Ced

1

保证

没有为任何阵列操作指定时间复杂度的保证。数组如何执行取决于引擎选择的基础数据结构。引擎也可能具有不同的表示形式,并根据某些启发式方法在它们之间切换。初始数组大小可能会或可能不会是这种启发式方法。

现实

例如,V8使用(截至今天)哈希表数组列表来表示数组。它还具有各种不同的对象表示形式,因此无法比较数组和对象。因此数组访问总是更好为O(n),并且可能甚至是一样快,一个C ++数组访问。追加为O(1),除非达到数据结构的大小且必须进行缩放(其间为O(n))。装腔作势更糟。如果您执行delete array[index](不要!)之类的操作,删除操作会更糟,因为这可能会迫使引擎更改其表示形式。

忠告

将数组用于数字数据结构。这就是他们的目的。这就是引擎将对其进行优化的原因。避免使用稀疏数组(或者,如果必须这样做,则可能会导致性能下降)。避免使用混合数据类型的数组(因为这会使内部表示更加复杂)。

如果您确实要针对某个引擎(和版本)进行优化,请查看其源代码以获取绝对答案。


等待一会儿,我们可以使用混合数据类型的数组吗?Javascript太酷了!
阿努拉格

确实是@Anurag,但在99%的情况下,您将不需要此功能
Desiigner
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.