在javascript中搜索字符串的最快方法

时间:2011-06-12 11:49:34

标签: javascript

我的页面上有一个隐藏字段,用于存储空格分隔的电子邮件列表。 我可以在该领域发送最多500封电子邮件。

如果该列表中已存在给定的电子邮件,那么搜索的最快方式是什么? 我需要在循环中搜索多个电子邮件

  1. 使用RegEx查找匹配

  2. 使用indexOf()

  3. 将列表转换为a 然后是javascript字典 搜索
  4. 如果这完全重复,请告诉我其他问题。 感谢

    编辑: 感谢大家的宝贵意见和答案。 基本上我的用户在db中有一个电子邮件列表(0-500)。 向用户显示他自己的联系人列表。 然后,用户可以从他的联系人列表中选择一个或多个电子邮件添加到列表中。 我想在客户端确保他没有添加重复的电子邮件。 整个操作由ajax驱动,因此需要jsvascript。

6 个答案:

答案 0 :(得分:10)

答案是:取决于

  • 这取决于您实际想要衡量的内容。
  • 这取决于您搜索的数量与搜索的数量之间的关系。
  • 这取决于JavaScript实现。不同的实现通常具有从根本上不同的性能特征。这是“不要过早优化”规则特别适用于跨实现JavaScript的众多原因之一。

...但是如果你要找的总数比总数少很多,那么它可能是String#indexOf,除非你可以创建一次字典并重复使用它(不只是这一个寻找X条目的循环) ,但每个循环寻找X条目,我倾向于怀疑是你的用例),在这种情况下,更快速地建立500键字典并使用它。

我将一个test case on jsperf放在一起比较查找埋藏在包含500个以空格分隔的唯一条目的字符串中的五个字符串的结果。请注意,jsperf页面比较了一些苹果和橙子(我们可以忽略设置和我们忽略的设置类型的情况),但jsperf很难分开它,我决定将其作为读者的练习。

在我对您认为自己正在做的事情的测试中,Chrome,Firefox,IE6,IE7和IE9最快String#indexOf。 Opera最快RegExp alternation。 (请注意,IE6和IE7没有Array#indexOf;其他人都这样做。)如果你可以忽略字典设置时间,那么使用字典就是最让人失望的赢家。

这是准备代码:

// ==== Main Setup
var toFind = ["aaaaa100@zzzzz", "aaaaa200@zzzzz", "aaaaa300@zzzzz", "aaaaa400@zzzzz", "aaaaa500@zzzzz"];
var theString = (function() {
 var m, n;

 m = [];
 for (n = 1; n <= 500; ++n) {
  m.push("aaaaa" + n + "@zzzzz");
 }
 return m.join(" ");
})();

// ==== String#indexOf (and RegExp) setup for when we can ignore setup
var preppedString = " " + theString + " ";

// ==== RegExp setup for test case ignoring RegExp setup time
var theRegExp = new RegExp(" (?:" + toFind.join("|") + ") ", "g");

// ==== Dictionary setup for test case ignoring Dictionary setup time
var theDictionary = (function() {
 var dict = {};
 var index;
 var values = theString.split(" ");
 for (index = 0; index < values.length; ++index) {
  dict[values[index]] = true;
 }
 return dict;
})();

// ==== Array setup time for test cases where we ignore array setup time
var theArray = theString.split(" ");

String#indexOf 测试:

var index;
for (index = 0; index < toFind.length; ++index) {
 if (theString.indexOf(toFind[index]) < 0) {
  throw "Error";
 }
}

String#indexOf(忽略设置)测试,我们忽略了在大字符串的两端放置空格的(小)开销:

var index;
for (index = 0; index < toFind.length; ++index) {
 if (preppedString.indexOf(toFind[index]) < 0) {
  throw "Error";
 }
}

RegExp更改测试:

// Note: In real life, you'd have to escape the values from toFind
// to make sure they didn't have special regexp chars in them
var regexp = new RegExp(" (?:" + toFind.join("|") + ") ", "g");
var match, counter = 0;
var str = " " + theString + " ";
for (match = regexp.exec(str); match; match = regexp.exec(str)) {
 ++counter;
}
if (counter != 5) {
 throw "Error";
}

RegExp交替(忽略设置)测试,我们忽略设置RegExp对象并在大字符串的任一端放置空格所花费的时间(我不知道)认为这适用于您的情况,您正在寻找的地址将是静态的:

var match, counter = 0;
for (match = theRegExp.exec(preppedString); match; match = theRegExp.exec(preppedString)) {
 ++counter;
}
if (counter != 5) {
 throw "Error";
}

词典测试:

var dict = {};
var index;
var values = theString.split(" ");
for (index = 0; index < values.length; ++index) {
 dict[values[index]] = true;
}
for (index = 0; index < toFind.length; ++index) {
 if (!(toFind[index] in dict)) {
  throw "Error";
 }
}

字典(忽略设置)测试,我们不用担心字典的设置时间;请注意, 不同 RegExp更改(忽略设置)测试,因为它假设整体 list是不变的:

var index;
for (index = 0; index < toFind.length; ++index) {
 if (!(toFind[index] in theDictionary)) {
  throw "Error";
 }
}

Array#indexOf 测试(请注意,一些非常古老的JavaScript实现可能没有Array#indexOf):

var values = theString.split(" ");
var index;
for (index = 0; index < toFind.length; ++index) {
 if (values.indexOf(toFind[index]) < 0) {
  throw "Error";
 }
}

Array#indexOf(忽略设置)测试,与词典(忽略设置)一样,假设整体列表是不变的:

var index;
for (index = 0; index < toFind.length; ++index) {
 if (theArray.indexOf(toFind[index]) < 0) {
  throw "Error";
 }
}

答案 1 :(得分:3)

您需要确保实际拥有正确的解决方案,而不是寻找最快的解决方案。因为有四种情况可能会出现电子邮件地址而且天真的搜索可能会失败:

  1. 独自:user@example.com
  2. 在开头:user@exampel.com …
  3. 最后:… user@example.com
  4. 介于:… user@example.com …
  5. 之间

    现在让我们分析每个变体:

    1. 要允许任意输入,您需要正确转义输入。您可以使用以下方法执行此操作:

      RegExp.quote = function(str) {
          return str.toString().replace(/(?=[.?*+^$[\]\\(){}-])/g, "\\");
      };
      

      要匹配所有四种情况,您可以使用以下模式:

      /(?:^|\ )user@example\.com(?![^\ ])/
      

      因此:

      var inList = new RegExp("(?:^| )" + RegExp.quote(needle) + "(?![^ ])").test(haystack);
      
    2. 使用indexOf稍微复杂一些,因为您需要手动检查边界:

      var pos = haystack.indexOf(needle);
      if (pos != -1 && (pos != 0 && haystack.charAt(pos-1) !== " " || haystack.length < (pos+needle.length) && haystack.charAt(pos+needle.length) !== " ")) {
          pos = -1;
      }
      var inList = pos != -1;
      
    3. 这个很简单:

      var dict = {};
      haystack.match(/[^\ ]+/g).map(function(match) { dict[match] = true; });
      var inList = dict.hasOwnProperty(haystack);
      
    4. 现在要测试哪种变体最快,你可以在jsPerf进行测试。

答案 2 :(得分:1)

indexOf()最有可能是最快的,请记住您需要搜索两种可能的情况:

var existingEmails = "email1, email2, ...";
var newEmail = "somethingHere@email.com";
var exists = (existingEmails.indexOf(newEmail + " ") >= 0) || (existingEmails.indexOf(" " + newEmail ) > 0);

答案 3 :(得分:0)

你问的问题有太多未说明的变量让我们回答。例如,您希望执行此搜索多少次?只有一次?一百次?这是一个固定的电子邮件列表,还是每次都改变?您是使用页面加载电子邮件还是通过AJAX加载?

如果您正在执行多个搜索,或者电子邮件已加载页面,那么您最好创建一个名称字典,并使用运算符中的Javascript

如果您从某些页外源获取字符串,并且只搜索一次,那么indexOf可能会更好。

在所有情况下,如果您真的关心速度,那么最好进行测试。

但后来我会问“为什么你关心速度?”这是一个网页,其中加载页面以网络速度发生;搜索以或多或少的本地处理器速度发生。这一次搜索不太可能在页面行为中产生明显的差异。

答案 4 :(得分:0)

这是一个小解释:

执行字典查找相对复杂 - 与有很多键时按键进行线性查找相比非常快,但比直接数组查找要复杂得多。它必须计算密钥的哈希值,然后确定应该在哪个桶中,可能处理重复的哈希值(或重复的桶),然后检查是否相等。

与往常一样,为作业选择正确的数据结构 - 如果你真的可以通过索引到一个数组(或列表),那么是的,这将是非常快的。

以上内容摘自@Jon Skeet的一篇博客文章。

答案 5 :(得分:0)

我知道这是一个老问题,但是对于那些将来可能需要的人来说,这是一个答案。

我做了一些测试,indexOf()方法速度非常快!

在Opera 12.16上测试了这个案例,需要216μs来搜索并找到一些东西。

以下是使用的代码:

console.time('a');
var a=((Math.random()*1e8)>>0).toString(16);
for(var i=0;i<1000;++i)a=a+' '+((Math.random()*1e8)>>0).toString(16)+((Math.random()*1e8)>>0).toString(16)+((Math.random()*1e8)>>0).toString(16)+((Math.random()*1e8)>>0).toString(16);
console.timeEnd('a');
console.time('b');
var b=(' '+a).indexOf(((Math.random()*1e8)>>0).toString(16));
console.timeEnd('b');
console.log([a,b]);

在控制台中,您将看到巨大的输出。

计时器'a'计算制作“垃圾”所需的时间,计时器'b'是搜索字符串的时间。

只需在电子邮件列表中添加2个空格,一个在前和后一个,并在电子邮件前后添加1个空格,即可开始使用。

我用它在没有jQuery的元素中搜索一个类,它的工作速度非常快。