OR 操作 ( || ) 和 inArray() 的性能比较

7
假设你想要检查用户在表单字段中输入的字符串。哪种方法是将其与可能值列表进行比较的最快方式?
以下示例使用jQuery。
第一种方法:使用||。
if (input == "firstValue" || input == "secondValue" || ... ) {
    ...
}

第二种方法:使用 inArray()
if ($.inArray(input, array) >= 0) {
    ...
}

这两种方法之间有显著的区别吗?

3
两个都具有O(n)的最坏情况复杂度,其中n是你要测试的物品数量,唯一的性能提升在于一个不需要进行函数调用,而另一个需要。 - Andreas Wong
2
你说的是真的 JavaScript 吗? - Niko
然而,在实际应用中,我更倾向于使用 inArray() 这样的函数,因为它具有更大的潜力来处理大型对比数据集。同时也更易于阅读。想象一下,如果我们需要对比10个或更多的值会是怎样的情况! :D - Richard Neil Ilagan
@Niko ~ 可能是jQuery。http://api.jquery.com/jquery.inarray/ - Richard Neil Ilagan
@Niko 哈哈,抱歉,标签打错了:D 已编辑 - user1301428
显示剩余2条评论
4个回答

17

你不需要最快的方法,而是最易读的方法。这时候使用 in_array() (JavaScript: array.indexOf(value) >= 0) 对于两个或三个以上的值来说更为合适。

性能差异可以忽略不计 - 尽管函数调用和数组创建肯定会有一些开销,但与文件访问、数据库访问、网络访问等昂贵的操作相比,这些开销微不足道。因此,在最终结果中,没有人会注意到区别。

这里是一个短小的基准测试,每个测试都执行了100万次:

5.4829950332642 - in_array, the array is recreated everytime
2.9785749912262 - in_array, the array is created only once
0.64996600151062 - isset(), the array way created only once and then the values were turned to keys using array_flip()
2.0508298873901 - ||

因此,最快且仍然非常易读的方法是这样的。除非你只创建$arr一次并使用多次,否则没有必要这样做,你可以简单地使用in_array()

$arr = array_flip(array('your', 'list', 'of', 'values'));
if(isset($arr[$value])) ...

如果您确实需要 JavaScript (在这种情况下去掉那些$前缀!), 最好的解决方案是使用Array.indexOf():

['a', 'b', 'c'].indexOf(value) >= 0

然而,并不是所有浏览器都已支持Array.indexOf(),所以您可能需要使用例如Underscore.js中的函数:

_.contains(['a', 'b', 'c'], value)

jQuery 也有一个函数可以实现这个功能:

$.inArray(value, ['a', 'b', 'c'])

最快的方法是使用对象和 in 运算符,但是对象的定义不如数组定义易读:

value in {'a':0, 'b':0, 'c':0}

以下是不同解决方案的JSPerf基准测试:http://jsperf.com/inarray-vs-or - 但是,大多数情况下性能差异较大的影响可以忽略不计,因为您不会在循环中执行代码数百万次。


1
如果所有的代码都是由您编写而不被视为遗留代码,那确实是一个好主意 - 如果您在某个地方使用了 for(var i in array)(您不应该这样做),扩展 Array.prototype 将会破坏这些循环。除此之外,OP 已经在使用 jQuery 了。 - ThiefMaster
2
性能差异只有在实际检查运行多次且数组始终相同时才相关,即您可以创建一个数组一次,然后重复使用它。 - ThiefMaster
我的意思是,我有点期望使用对象会更快。 - user1301428
inArray 是一种函数,每次都必须调用它,引擎不知道它只是检查数组中的一个字符串是否为给定值。val == '..' || ... 代码将被解析一次,然后所有内容都在引擎的本机代码中完成——在计算表达式时没有涉及 JavaScript 代码。 - ThiefMaster
在这里添加了underscore和lodash的测试,以进行比较:http://jsperf.com/inarray-vs-or/3基本上,在Chrome中,lodash.contains的性能实际上与arr.indexOf相当,但在IE中,indexOf仍然获胜。 - rosswil
显示剩余7条评论

4
答案是,取决于具体情况...
如果只有几种可能性,使用 if (a || b || c)
如果可能有多达 10 种,使用 Array.indexOf()
请注意,对于上述两个建议,选择应该取决于可读性,而不是性能。
如果超过了这些数量,使用一个键等于值的Object,然后您可以使用 if (myVar in myKeyObj)。 这应该会提供最差 O(log n) 的性能。

我认为他错误地标记了这个问题。他的代码是PHP,而不是JavaScript。 - ThiefMaster
@ThiefMaster 你为什么认为这是 PHP?因为他使用了美元符号前缀吗?:) 话虽如此,他确实依赖于 inArray 的布尔结果... 这很令人困惑。 - Ja͢ck
我已经编辑了问题,以明确我正在使用jQuery。 - user1301428
O(log n)?假设JavaScript使用哈希表进行对象键查找,那么它应该是O(1),不是吗? - ThiefMaster
@ThiefMaster 这将取决于具体的实现 - 我怀疑 ES5 标准对此并没有规定。 - Alnitak

2

在大多数语言中,inArray()的实现方式如下:

function inArray(needle, haystack) {
  for (i = 0; i < length; i++) {
    if (haystack[index] == needle) {
      return true;
    }
  }

  return false;
}

如果您展开该循环,最终会执行
if (haystack[0] == needle) {
  return true;
} 
if (haystack[1] == needle) {
  return true;
}
if (haystack[3] == needle) {
  return true;
}
// and so on

这可以简化为

if (haystack[0] == needle || haystack[2] == needle || … ) {
  return true;
}

不改变底层的发生情况。


如果你经常需要查找这样的东西,我建议你了解一下地图,而不是

var haystack = ['hello', 'world', 'how', 'is', 'life'];
if (inArray("life", haystack)) {
  // …
}

你需要做的是

var haystack = {'hello' : true, 'world' : true, 'how' : true, 'is' : true, 'life' : true};
if (haystack["life"]) {
  // …
}

检查的元素越多,与数组相比,地图的性能将会更好。


如果我需要检查不到100个元素,那么我会感受到性能的提升吗? - user1301428
http://jsperf.com/map-or-array 显示,当数组中有10个元素时,inArray()更快,但当有26个元素时,它已经比inMap()慢了。请注意,inArray()的性能与数组中元素的数量和位置(如果可以找到)相关。另一方面,inMap()的性能不受要比较的项目的影响。 - rodneyrehm
好的,所以Chrome和Firefox在使用inArray()函数时对于10个元素的速度存在分歧。无论如何,你应该能够理解这个情况... - rodneyrehm

2

在编写 JavaScript 时,通常性能不是一个大问题,并且通常很难回答。但在这个示例中,解决方案之间的关键区别在于扩展性。第一种解决方案总是会执行预定数量的比较,而 inArray 解决方案的扩展性较差,因为如果有更多的值,它将进行更多的比较。

然而,我仍然会选择 inArray,99.99% 的情况下,性能真的并不重要。您希望保持代码的可维护性,这更重要。


第一个解决方案总是会进行预定数量的比较。我认为如果在任何 || 中得到 true,JavaScript / PHP 会提前退出,对吗? - Andreas Wong
正确,这样最大值就会被确定。 - TJHeuvel
啊,现在我明白你所说的“预设”的意思了。谢谢。 - Andreas Wong
哦,你的意思是执行的最大检查数? - user1301428
顺便说一下,如果有更多的值,第一个解决方案也会进行更多的比较。那么你为什么说第二个解决方案的扩展性更差呢? - user1301428
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接