当用作哈希时,JavaScript 的数组的大 O 是多少?
Posted
技术标签:
【中文标题】当用作哈希时,JavaScript 的数组的大 O 是多少?【英文标题】:What's the big O for JavaScript's array when used as a hash? 【发布时间】:2011-04-20 23:31:44 【问题描述】:当用作哈希时,javascript 的数组访问的大 O 是什么?
例如,
var x= [];
for(var i=0; i<100000; i++)
x[i.toString()+'a'] = 123; // using string to illustrate x[alpha]
alert(x['9999a']); // linear search?
希望 JS 引擎内部不会使用线性搜索 O(n),但这是肯定的吗?
【问题讨论】:
没有什么是“肯定的”(除非像 C++ 一样,标准定义了容器的性能特征?)但我可以保证没有浏览器使用线性搜索。如今,浏览器在 JS 基准测试中的竞争非常激烈。您可以放心,索引数组的速度将与浏览器制造商的速度一样快。 【参考方案1】:在 JavaScript 中访问对象属性和数组元素在语法上假定在 constant time: O(1) 中完成。 ECMAScript 规范中不保证性能特征,但所有现代 JavaScript 引擎都在恒定时间内检索对象属性。
下面是一个简单的示例,展示了当容器大 1000 倍时访问时间如何增长:
var largeObject = ;
var smallObject = ;
var x, i;
for (i = 0; i < 1000000; i++)
largeObject['a' + i] = i;
for (i = 0; i < 1000; i++)
smallObject['b' + i] = i;
console.time('10k Accesses from largeObject');
for (i = 0; i < 10000; i++) x = largeObject['a' + (i % 1000000)];
console.timeEnd('10k Accesses from largeObject');
console.time('10k Accesses from smallObject');
for (i = 0; i < 10000; i++) x = largeObject['a' + (i % 1000)];
console.timeEnd('10k Accesses from smallObject');
Firebug、Firefox 3.6.10(Mac OS X 10.6.4 - 2.93Ghz Intel Core 2 Duo)中的结果:
10k Accesses from largeObject: 22ms
10k Accesses from smallObject: 19ms
Chrome 开发工具 6.0.472 中的结果:
10k Accesses from largeObject: 15ms
10k Accesses from smallObject: 15ms
Windows 7 上带有 Firebug Lite 的 Internet Explorer 8.0.7600
10k Accesses from largeObject: 250ms
10k Accesses from smallObject: 219ms
【讨论】:
您能指出 ECMAScript 或 JavaScript 规范中的段落,即访问数组元素或对象属性保证为 O(1) 吗? AFAIK,JScript 具有完全符合标准的 O(n) 对象实现作为简单的链表。 @Jörg:规范中没有提到它,所以在实践中它是依赖于实现的......这就是为什么我选择说它是“句法假设的”。不过,为了更准确,我重新表述了我的答案。 @Chaos:至少他们没有将对象属性实现为链表。 O(1) 是平均情况。哈希表的最坏情况是 O(n)。 我在 Node.js 上运行代码。 V8 确实很好。谢谢你的帖子。来自 largeObject 的 10k 次访问:7ms 来自 smallObject 的 10k 次访问:6ms【参考方案2】:首先数组实际上是哈希。 始终。这就是为什么x[5] === x["5"]
:
var x = [];
x[5] = 10;
alert( x[5] === x["5"] ); // true
对象是散列,而数组只是特殊对象。如果你想使用一般散列,请选择对象。 Javascript 中的“关联数组”是对象。数组用于数字索引数据。数组有一个length
属性和类似数组的方法,如push
、pop
、sort
等,这对于散列没有意义。
至于在对象中搜索的大 O:它依赖于实现。
您可以做的最好的两件事可能是:
查看部分浏览器实现的源码
为大 n 做一些基准测试并得出结论
language specification的相关部分:
4.3.3 对象
对象是属性的集合 并且有一个原型对象。
8.6.2 对象内部属性和方法
数组对象有一个略 不同的实现方式 [[DefineOwnProperty]] 内部方法。 数组对象给予特殊处理 到某一类属性名称。
【讨论】:
数组不是散列。x["5"]
中的值在查找之前被转换为 number
。这就是为什么x[5]
也会得到相同结果的原因。但是,Array
在其原型链中确实有 Object
,因此它不是 JavaScript 和 typeof === "object"
中的类型。它的转换类似于~~"5"
,如果它等于0,则数组保持不变,例如x["a5"]
。
我提供了我的参考资料(语言规范),你可以展示什么作为你完全主观和错误理论的基础?这就是问题开始的地方。
(es5.github.com/#x15.4) 1.)
数组是散列,"15.4 数组对象:数组对象对某一类属性名给予特殊处理." 2.)
属性名称(甚至数组索引)被视为字符串:“属性名称 P(以 字符串值 的形式)是数组索引当且仅当 ToString(ToUint32(P)) 等于 P 且 ToUint32(P) 不等于 232−1。" 3.)
它不会像~~
因为它是一个双位运算符,您从某种字节剃须优化文章中获得了它,并且与规范相去甚远。
它的转换与上面引用的完全一样:if ToString(ToUint32(P)) equals P
,其中 P 是属性名称。
不,它们是哈希值,因为它们将属性视为字符串(正如您可以在引用的规范中阅读的那样)。我不会再写下来了。这一切都在那里。您只需要阅读并仔细阅读即可。以上是关于当用作哈希时,JavaScript 的数组的大 O 是多少?的主要内容,如果未能解决你的问题,请参考以下文章
变长数组 + 哈希表解决“O 时间插入删除和获取随机元素”问题
变长数组 + 哈希表解决“O 时间插入删除和获取随机元素”问题