【问题标题】:Object.keys() complexity?Object.keys() 复杂性?
【发布时间】:2011-10-10 18:04:19
【问题描述】:

有人知道 ECMAScript5 的 Object.keys() 在常见实现中的时间复杂度吗? O(n)n 键吗?假设哈希实现,时间是否与哈希表的大小成正比?

我正在寻找语言实现者的保证或一些现实世界的基准测试。

【问题讨论】:

  • 您希望有多少个键,这样枚举它们的时间复杂度很重要?
  • 我觉得不能小于O(n)
  • @PabloFernandez,长度小于 O(n)
  • @IAbstractDownvoteFactory:虽然函数Object.keys() 可能能够在O(1) 中返回,但枚举结果 不能在小于O(n) 的时间内完成。
  • @Gabe,一百万。这会改变答案吗?

标签: javascript performance time-complexity ecmascript-5


【解决方案1】:

至少在 V8(chrome、node.js)中似乎是 O(n)

> var hash = {}
>   ,    c = 0;
> 
> var s = +new Date();Object.keys(hash);console.log(+new Date() - s);
0
> for(var i=0; i<100000; i++, c++){ hash[c] = 1; }
> var s = +new Date();Object.keys(hash);console.log(+new Date() - s);
26
> for(var i=0; i<100000; i++, c++){ hash[c] = 1; }
> var s = +new Date();Object.keys(hash);console.log(+new Date() - s);
49
> for(var i=0; i<100000; i++, c++){ hash[c] = 1; }
> var s = +new Date();Object.keys(hash);console.log(+new Date() - s);
75
> for(var i=0; i<100000; i++, c++){ hash[c] = 1; }
> var s = +new Date();Object.keys(hash);console.log(+new Date() - s);
102    

【讨论】:

  • 这些结果对于密集哈希映射是有意义的。键越稀疏,性能会下降吗?
  • @hurrymaplelad - 什么?所有 JS 哈希键都是字符串。此代码有效地生成{'1':1, '2':1, '3':1, ...} sparse vs dense 键对哈希实现没有意义,只有数组。而且引擎将哈希实现为数组确实没有任何意义,因为数字索引通常相当罕见。虽然如果你出于某种原因想要测试它,只需将 c++ 更改为 c+=Math.random(),这将为你提供完全不可关联的键。
  • @cwolves:Array 对象只是一个其属性应为整数的对象。这些并不少见,而且肯定有一些 JS 实现使用数组来支持 Array 实例。
  • @cwolves: expanded your test 涵盖密钥大小大小的变化,结果看起来一致。你是对的,sparse vs dense 不适用于你的测试。我正在寻找load factor 较低的示例,但这与关键选择无关。 JS 对象通常是作为 hashmap 实现的吗?如果是,性能是O(n+s),其中s是哈希表的大小?
  • JS 对象不是作为 hashmaps 实现的,而是作为数组对(即 C 数组,不是 JS Array 对象)code.google.com/intl/de-DE/chrome/devtools/docs/…
【解决方案2】:

(这里是 V8 开发人员。)

Mark Kahn 的答案对于足够密集的整数键控/“索引”属性是正确的,其中Object.keys() 的复杂度确实是 O(n)。

虽然 JavaScript 规范假装所有对象属性都是字符串键控/“命名”,但这并不是现代高性能引擎实现它的方式。内部差别很大!索引属性存储在一个数组中(只要它们足够密集),这通常比{'1': 1, ...} 字典提供更好的性能很多

对于具有数千个 named 属性的对象,我们的实现确实使用了哈希表(正如问题所猜测的那样),这意味着 Object.keys() 的复杂度为 O(n log n) 。那是因为哈希表(当然)按自己的顺序存储条目。 Object.keys() 必须按照创建的顺序返回命名属性,我们将其存储为附加元数据,这意味着我们必须在从哈希表中检索键后对键进行排序,这是一个 O(n log n) 操作.

在实践中出现的大多数对象上的命名属性(最多大约一千个属性)(通常)按创建顺序存储在一种特殊的内部数组中,因此可以在 O 中检索它们(n) 并且不需要排序。

所以总结真的是“视情况而定”:-)

【讨论】:

  • 嗯,但这里的问题是,如果我使用Object.keys(),它会是O(n) 还是别的什么?我没有完全理解你的答案......用天真的术语来说,如果我有一个如下的哈希映射:{a:1,b:2,...} 和我使用Object.keys() 相同,时间复杂度会是O(n) 还是别的什么?另外,如果我们只想获取键而不是排序呢?
  • 没有排序就无法获取对象的键。正如我所写,Object.keys() 的复杂度是 O(n) 还是其他取决于您的对象的细节(当然,也取决于您碰巧正在运行的特定 JavaScript 引擎的特定版本的实现细节在)。没有任何保证。
  • 这个截止值是 20,您可以使用其他答案中的代码并使用非常小的对象进行验证,但多次调用 Object.keys
  • @DaveAnkin:如果这么简单,我会这么说的。没那么简单。它会随着时间/版本/实现而变化。这通常也无关紧要:对于大多数常见情况,假设Object.keys() 具有线性复杂度是合理的(即使在所有 情况下都不是这样)。请注意,“来自其他答案的代码”仅说明了一种特定情况,而不是所有可能的情况。
  • 我很好奇 Object.keys(bigObject).length 是否仍然是 O(n) 或者是 js 优化器可以依赖的东西来确保这是 O(1)
猜你喜欢
  • 1970-01-01
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-16
  • 1970-01-01
  • 2016-08-03
  • 1970-01-01
相关资源
最近更新 更多