【问题标题】:What is the fastest way to compare strings in JavaScript?在 JavaScript 中比较字符串的最快方法是什么?
【发布时间】:2013-11-08 15:06:08
【问题描述】:

我有两个字符串,我需要知道它们是否相等。

我以前这样做过: str1 === str2 ,但我想知道是否有更快的方法来比较两个字符串。

字符串相当短,只有 15-25 个字符长。我的问题是我正在遍历很多字符串并且需要很长时间。

我在这样的结构中有很多比较:

If(str === str1)
{
  do something
}
else if(str === str2)
{
  do something
}
else if(str === str3)
{
  do something
}

字符串没有任何共同的结构或分组。

【问题讨论】:

  • 就是这样。
  • 字符串有多长?如果它们太长,你可以散列它们并比较它们的散列
  • 你们为什么不赞成这个?这可能是一个有趣的问题。
  • @DavidW 如果你需要重复比较并且你可以保存/缓存哈希字符串,那么它会更快,在其他情况下你是绝对正确的......
  • “我的问题是我要遍历很多字符串,而且需要很长时间。”你以前应该说过这个。测试多个字符串之一的匹配允许许多不同的解决方案,而这些解决方案在简单地测试一对随机字符串时是不可能的。为了得到正确的答案,你应该发布代码,或者至少是它的大纲,因为它仍然不清楚你的意思。

标签: javascript string performance string-comparison


【解决方案1】:

a === b比较字符串是比较字符串原生的最快方法。

但是,如果您可以创建像 new String("test") 这样的字符串对象,重新使用它们并在比较中使用它们,那会更快,因为 JS 引擎只需要进行指针比较,即 (少量)比字符串比较快。

http://jsperf.com/string-vs-object-comparisons

【讨论】:

  • 这是一个有趣的想法。谢谢
  • 因为JS引擎可能会通过string interning进行优化。
  • 这里不测试字符串的值,只是字符串在内存中的位置new String("apple") === new String("apple")等于false
  • 是的,这就是为什么“重用这些”是这里的关键。您不应该创建新的,而是重新使用现有的,例如使用各种地图。
  • 在这种情况下,您根本不需要字符串。任何普通物体都可以。如果您确实想使用字符串,我建议将其设为Symbol。这就是符号的意义所在。
【解决方案2】:

如果您的“做某事”具有不同值的相似形式,您可以将值放入映射并使用字符串作为键。例如,假设您必须处理许多具有不同长度单位的数字,并且您希望将它们全部转换为米:

var conversionToMeters = {
    "inch":   0.0254,
    "inches": 0.0254,
    "foot": 0.3048,
    "feet": 0.3048,
    "cubit":  0.4572,
    "cubits": 0.4572,
    "yard":  0.9144,
    "yards": 0.9144,
    "kilometer":  1000,
    "kilometers": 1000,
    "mile":  1609.344,
    "miles": 1609.344,
    "lightyear":  9.46e15,
    "lightyears": 9.46e15,
    "parsec":  3.09e16,
    "parsecs": 3.09e16,
}

(为简洁起见,省略了缩写(如“km”)和国际拼写(如“kilometres”)。)您可以提前准备该地图以避免创建开销。现在,给定一个变量length,例如length = "80 miles",你可以这样做:

var magnitude = length.replace(/[\D]/g, "");
var unit = length.replace(/[\d\s]/g, "");
var lengthInMeters = magnitude * conversionToMeters[unit];
alert(lengthInMeters + " meters"); // Ta-da!

如果您的“做某事”不共享公共代码,您仍然可以使用地图,但它将是功能地图:

var actions = {
    "eat": function() {
        if (spareFood > 0) {
            spareFood--;
            energy += 10;
            health++;
            alert("Yum!");
        }
    },
    "walk": function() {
        if (energy > 0) energy--;
        // ...
    },
    "attack": function() {
        if (energy > 0) {
            if (Math.random() < 0.25) {
                health--;
                alert("Ouch!");
            }
            energy--;
        }
    },
    // ...
};

这是一个有点傻的例子,但我希望它能解释基本思想。这些操作同样可以是 XML 标签,或者虚拟机中 CPU 指令的名称,或者有特殊运输要求的产品名称,等等。获得 action 变量后,执行它就像这样简单:

actions[action]();

地图并不是做这种事情的唯一方法。您的原始 if/else 示例可以通过将 if 嵌套在 旨在快速消除大多数候选字符串的其他 if 中轻松优化。

您所依据的标准将取决于您正在使用的确切字符串。它可以是字符串的长度,也可以是第一个字母,或者是几个最有区别的字母:

if (str.length === 3) {
    // test all length 3 strings here
    if (str === strA) doSomething();
    else if (str == strB) doSomething();
} else if (str.length === 4) {
    // test all length 4 strings here
    if (str === strC) doSomething();
    else if (str === strD) doSomething();
}

或者:

var first = str[0]; // first character
if (first >= "0" && first <= "9") {
    // test all strings that start with digits here
if (first >= "a" && first <= "l") {
    // test all strings that start with letters
    // in the first half of the alphabet here
} else if (first >= "m" && first <= "z") {
    // test all strings that start with letters
    // in the latter half of the alphabet here
}

您可以将这些类型的测试相互嵌套到任何适合筛选您正在使用的特定字符串的程度。这是一种展开的binary search,尽管您所依据的标准不必将候选字符串完全分成两组。

此外,当您像这样使用 if/elseif 时,通常值得按频率降序排列字符串。即,首先测试发生最多的那些。如果只有几个字符串构成了大部分数据,请将它们拉到顶部,甚至将它们放在任何基于长度或首字母的预测试之外。

您必须决定是否值得做这些事情:如果您将这些技术发挥到极致,您可能会获得微小的额外性能优势,但会牺牲可读性和可维护性。

附:我不太了解 JavaScript,无法确切知道这些技术将如何执行,但我在 Java 中做过类似的事情。在 Java 中,当“做某事”需要不同的值但可以使用相同的代码时,映射方法是无与伦比的。在另一个程序中,我需要在一个整数值上 switch 执行大约 400 个不同的操作(这太糟糕了)。 HotSpot 客户端 VM 对 switch 语句的执行效率极低,这只是很多 elseif,而且速度太慢。一组函数(从技术上讲是具有重写虚方法的对象)更快,但是与每个操作的简单性相比,函数调用开销太大了。在这种情况下,我发现混合二元四元搜索是有效的。这意味着:外部测试是将输入值平均分为两组的 if/else。这些被嵌套,直到内部组中只剩下四个可能的值。然后我使用了 if/elseif/elseif/else 来区分剩余的四个值。由于这很长,我写了一些代码来为我编写它,但对于这个特定的应用程序来说仍然值得付出努力。

附言我在上面跳过了一种方法,但为了完整起见,我将其包括在内:如果您的字符串很少需要更改,您可以使用perfect hash function。有一些实用程序可以为您设计这些功能:只需为它们提供所有字符串的列表。一个完美的散列函数将从一个字符串中计算出一个整数散列码,并保证您的集合中没有两个字符串具有相同的散列码。然后,您可以使用整数哈希码在数组中查找操作。它对解析编程语言的关键字等事情很有帮助。在更接近金属的语言中它可以更快,但在 JavaScript 中我怀疑它不值得。我提一下以防万一。

【讨论】:

    【解决方案3】:

    我已经做了一个基准测试槽https://jsbench.me/。这些哪里的结果:

    所以,正如Jack Giffin 所说,在这种情况下,switch 语法是最快的。如果你在做负比较器,那么结果就会改变:

    【讨论】:

      【解决方案4】:

      最快的 V8 方法是使用如下 switch 语句:

      var str = '' + prompt('Enter cat or enter in dog');
      switch(''+str){ // make it clear you are switching on a string
        case 'cat':
          console.log('you selected cat!');
          break;
        case 'dog':
          console.log('you selected dog!');
          break;
        default:
          console.log('you selected something else!');
      }

      为什么这是最快的方法是因为它会给 JIT 优化器更多的机会来优化比较。例如,它可能执行的一种可能的优化是在进行任何实际比较之前抢先搜索以相同字符开头的相同长度的字符串。

      但是,如果您进行这些 if-else 比较,那么 JIT 优化器可能会也可能不会将这些比较优化为有效的东西。

      之所以允许JIT优化器对switch语句进行自己的优化可以更快,是因为当它只是比较长度时,它将能够对它比较的字符串的长度进行排序。这将使数字长度比较快得多(请参阅Proccessing Sorted VS Unsorted Array)。

      【讨论】:

      • 什么是“JIST 优化器”?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-15
      • 1970-01-01
      • 1970-01-01
      • 2019-03-06
      • 2011-01-04
      • 1970-01-01
      相关资源
      最近更新 更多