当前位置: 首页 > 面试题库 >

为什么有时array.push比array [n] = value快?

萧鸿轩
2023-03-14
问题内容

作为测试某些代码的附带结果,我编写了一个小函数来比较使用array.push方法与直接寻址(array [n] =
value)的速度。令我惊讶的是,推送方法通常显示出更快的速度,尤其是在Firefox和Chrome中。出于好奇:有人对此有解释吗?您可以找到测试(单击“数组方法比较”)


问题答案:

各种各样的因素都在起作用,大多数JS实现使用平面数组,如果以后需要的话,可以转换为稀疏存储。

基本上,决定稀疏的决定是基于设置哪些元素以及要浪费多少空间才能保持平坦的启发式方法。

在您的情况下,您要首先设置最后一个元素,这意味着JS引擎将看到一个数组,该数组需要具有一个长度,n但只有一个元素的长度。如果n足够大,则将立即使该数组成为稀疏数组-
在大多数引擎中,这意味着所有后续插入将采用慢速稀疏数组的情况。

您应该添加一个额外的测试,在其中填充从索引0到索引n-1的数组-它应该快得多。

为响应@Christoph并出于拖延的愿望,这里描述了如何在JS中(通常)实现数组-具体情况因JS引擎而异,但一般原理是相同的。

所有JS Object(而不是字符串,数字,true,false undefinednull)都不继承自基本对象类型-
确切的实现有所不同,可能是C ++继承,也可能是在C中手动实现(以任何一种方式这样做都有好处)-基本的Object类型定义默认的属性访问方法,例如

interface Object {
    put(propertyName, value)
    get(propertyName)
private:
    map properties; // a map (tree, hash table, whatever) from propertyName to value
}

此Object类型处理所有标准属性访问逻辑,原型链等。然后Array实现变为

interface Array : Object {
    override put(propertyName, value)
    override get(propertyName)
private:
    map sparseStorage; // a map between integer indices and values
    value[] flatStorage; // basically a native array of values with a 1:1
                         // correspondance between JS index and storage index
    value length; // The `length` of the js array
}

现在,当您在JS中创建数组时,引擎将创建类似于上述数据结构的内容。当您将对象插入Array实例时,Array的put方法将检查属性名称是否为0到2 ^
32之间的整数(或可以转换为整数,例如“ 121”,“ 2341”等)。 -1(或者可能是2 ^
31-1,我完全忘记了)。如果不是,则将put方法转发到基本Object实现,并完成标准的[[Put]]逻辑。否则,将值放入数组自己的存储中,如果数据足够紧凑,则引擎将使用平面数组存储,在这种情况下,插入(和检索)仅是标准数组索引操作,否则引擎将转换数组稀疏存储,并放置/获取使用映射来从propertyName到值位置。

老实说,我不确定在发生这种转换后当前是否有任何JS引擎从稀疏存储转换为平面存储。

顺便说一下,这是对所发生情况的一个较高层次的概述,并省略了一些更棘手的细节,但这是一般的实现模式。引擎之间如何增加存储以及如何分配/放置如何分配的细节各不相同-
但这是我可以真正描述的最清晰的设计/实现方式。

一个较小的附加点,尽管ES规范指的propertyName是字符串JS引擎也倾向于专门处理整数查找,所以someObject[someInteger]如果您查看的是具有整数属性的对象,则不会将整数转换为字符串。数组,字符串和DOM类型(NodeLists等)。



 类似资料:
  • 问题内容: 在Java中,表达式为: 似乎等于: 尽管是有效的一元运算符,其优先级高于中的算术运算符。因此,编译器似乎假设该运算符不能为一元运算符,并解析该表达式。 但是,表达式: 即使存在以下唯一有效的解决方案,也不会编译: 和被指定为具有相同的优先级,那么为什么编译器为支持算术而解决看似模棱两可的问题,但为什么不这样做呢? 问题答案: 首先使用最大修改规则将文件标记化(转换为标记序列)-始终获

  • 问题内容: 我试图实施Miller- Rabin素数测试 ,并且对为什么中型数字(〜7位数字)花费如此长时间(> 20秒)感到困惑。我最终发现以下代码行是问题的根源: (其中,和都是相似的,但不相等的中号,是幂运算符,并且是模运算符) 然后,我尝试将其替换为以下内容: 相比之下,它几乎是瞬时的。 对于上下文,这是原始功能: 定时计算示例: 输出(与PyPy 1.9.0一起运行): 输出(在Pyth

  • 查询数组中元素,返回下标。 参数 名称 类型 默认值 描述 array [] 数组。 value * 寻找的对象。 返回值 下标,类型:number。

  • 问题内容: java.util.Random源代码的第294行说 为什么是这样? 问题答案: 该描述并不完全准确,因为0不是2的幂。更好的说法是 当n是2的幂或2的幂的负数或零时。 如果n是2的幂,则二进制中的n是单个1,后跟零。-n为2的补数是倒数+ 1,因此位排成一行 要了解其工作原理,请将二进制补码视为逆+ 1。 因为当您添加一个得到两个的补码时,您会一直进行到一个。 如果n不是2的幂,则结

  • 问题内容: 我编写了两种方法的代码,以找出LeetCode字符串中的第一个唯一字符。 问题陈述: 给定一个字符串,找到其中的第一个非重复字符并返回其索引。如果不存在,则返回-1。 示例测试用例: s =“ leetcode”返回0。 s =“ loveleetcode”,返回2。 方法1(O(n))(如果我错了,请纠正我): 方法2(O(n ^ 2)): 在方法2中,我认为复杂度应为O(n ^ 2

  • 问题内容: 在PHP中最好使用附加数组成员的方法, 要么 ? 尽管手册说您最好避免进行函数调用,但我阅读的速度也比慢得多。有哪些澄清或基准? 问题答案: 没有基准,但是我个人觉得看起来更干净,并且诚实地在毫秒内拆分头发是完全没有关系的,除非您计划将数十万个字符串添加到数组中。 编辑 :运行此代码: 第一种方法使用的速度比第二种方法快50%。 一些基准测试结果: 这并不奇怪,因为PHP手册指出了这一