【问题标题】:Why Javascript ===/== string equality sometimes has constant time complexity and sometimes has linear time complexity?为什么 Javascript ===/== 字符串相等有时具有恒定时间复杂度,有时具有线性时间复杂度?
【发布时间】:2014-12-20 09:32:34
【问题描述】:

在我发现常见/最新的 Javascript 实现使用字符串实习来提高性能 (Do common JavaScript implementations use string interning?) 之后,我认为字符串的 === 将获得恒定的 O(1) 时间。所以我对这个问题给出了错误的答案:

JavaScript string equality performance comparison

由于根据该问题的 OP 它是 O(N),因此将字符串输入加倍会使相等所需的时间加倍。他没有提供任何 jsPerf,因此需要进行更多调查,

所以我使用字符串实习的场景是:

var str1 = "stringwithmillionchars"; //stored in address 51242

var str2 = "stringwithmillionchars"; //stored in address 12313

“stringwithmillionchars”将被存储一次,比如说在内存的地址 201012 并且 str1 和 str2 都将“指向”这个地址 201012。然后可以使用某种散列来确定这个地址,以映射到内存中的特定位置。

所以在做的时候

"stringwithmillionchars" === "stringwithmillionchars"

看起来像

getContentOfAddress(51242)===getContentOfAddress(12313)

201012 === 201012

这将花费 O(1)/恒定时间

JSPerfs/性能更新:

即使字符串长 16 倍,JSPerf 似乎也显示恒定时间?请看:

http://jsperf.com/eqaulity-is-constant-time

上面的字符串可能太小了: 这可能显示线性时间(感谢 sergioFC)字符串是用循环构建的。我尝试不使用函数 - 仍然是线性时间 / 我稍微改变了一点 http://jsfiddle.net/f8yf3c7d/3/ .

根据https://www.dropbox.com/s/8ty3hev1b109qjj/compare.html?dl=0(sergioFC 制作的 12MB 文件),当您有一个字符串并且无论 t1 和 t2 有多大(例如 5930496 个字符)都已经在引号中分配了值(例如 5930496 个字符)时,它会将其设为 0- 1ms/瞬间。

似乎当您使用 for 循环或函数构建字符串时,该字符串不会被保留。因此,只有当您直接分配带有 var str = "test"; 之类的引号的字符串时才会发生实习

【问题讨论】:

  • 我认为是因为 === 运算符仅在比较对象时才比较内存地址(类似于 Java)。但是“某物”不是对象,它的类型是内置字符串。和比较数字一样,var a=2; var b=2;,如果你这样做 a===b 你不是在比较对象也不是内存地址。
  • 我知道你可以做 var str = new String("test"); 但我也不知道那里的含义..
  • 即使这样做 typeof str 也会是“字符串”,而不是对象。
  • 我已经删除了小提琴,以免压垮更多的浏览器。我认为它们太小了。重要提示:我刚刚测试了两个等于字符串与使用 var s1='...';var s2='...'; 构造的 5930496chars 的比较需要 0ms,而比较char 构造的相同字符串需要 20 毫秒。
  • 不知道,我对实习不熟悉。字符串太长以至于文件为 12MB。我要把它上传到 Dropbox 并用链接更新这条评论。

标签: javascript string performance time-complexity string-interning


【解决方案1】:

根据ECMAScript 5.1 Specification's Strict Equal Comparison algorithm,即使被比较的Objects的类型是String,也会检查所有的字符是否相等。

  1. 如果Type(x)是String,则返回true 如果xy是完全相同的字符序列(长度相同,对应位置的字符相同);否则,返回false

实习严格来说是一种实现方式,以提高性能。语言标准在这方面没有强加任何规则。因此,是否实习字符串取决于规范的实施者。

【讨论】:

  • 谢谢。您能想出一个比将字符串作为对象二处理更好的选择的原因吗?有这样的开销吗?
  • 我看不出这如何迫使引擎实际进行完整的字符串比较。如果两个变量指向保存在内存某处的同一个字符串,那么它们将满足这个要求。
  • @MichailMichailidis 实习严格来说是一种实现方式,以提高性能。语言标准在这方面没有强加任何规则。
  • 好吧,我们谈论的是最新/常见的实现——它真的被使用了吗?可以请更熟悉 jsperf 的人制作一个吗? Case1:“test”==="test",Case2:“testtest”==="testtest"(长度加倍) Case3:字符串不同但长度相同,Case 4:长度不同但相同。
  • @thefourtheye 不,抱歉。我根本没有提到实习。您的意思是“实现仍然必须检查所有字符”,我不同意它遵循您引用的部分。您无需完全比较即可实现“相同长度/相同字符”。
【解决方案2】:

基于字符串 a 和 b 的所有性能测试(参见原始帖子),a === b 的操作需要:

  • 常数时间 O(1) 如果字符串被保留。从这些示例看来,实习发生在直接分配的字符串(如var str = "test";)上,而不是如果您使用 for 循环或函数通过串联构建它。

  • 线性时间 O(N),因为在所有其他情况下,首先比较两个字符串的长度。如果相等,则我们进行逐字符比较。否则,它们当然不相等。 N是字符串的长度。

【讨论】:

    【解决方案3】:

    首先,很高兴看到一个 JSPerf 测试证明了将字符串大小加倍会使执行时间加倍的说法。

    接下来,让我们认为这是理所当然的。这是我的(未经证实、未经检查、可能与现实无关)的理论。

    无论引用多少数据,比较两个内存地址都很快。但是你必须先实习这个字符串。如果你的代码中有

    var a = "1234";
    var b = "1234";
    

    那么引擎首先要明白这两个字符串是相同的,并且可以指向同一个地址。因此,至少必须对这些字符串进行一次完全比较。所以基本上这里有以下选项:

    • 引擎在解析代码时直接比较和实习字符串。在这种情况下,equals 字符串应该获得相同的地址。
    • 引擎可能会说“这些字符串很大,我不想实习”并且有两个副本。
    • 引擎稍后可能会保留这些字符串。

    在后两种情况下,字符串比较会影响测试结果。在最后一种情况下 - 即使字符串最终被保留。

    但正如我所写,对于理论的圣人而言,这是一个狂野的理论。我首先想看看一些 JSPerf。

    【讨论】:

    • 我同意你的看法 - 另一个问题的原始发帖人说了,我认为这是理所当然的 - 如果他不这样做,我将很快准备一个 JSPerf 来详细说明这些数字。
    猜你喜欢
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多