哪个更快:在 PHP 中使用 in_array() 函数还是一堆表达式?

19

做以下操作会更快吗:

 if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') { ... }
或:
 if (!in_array($var, array('test1', 'test2', 'test3', 'test4') { ... }

在某些值的情况下,使用其中一种方法会更快吗?

(在这种情况下,第二个选项中使用的数组尚不存在。)


有点离题——你刚才删除了几分钟前的另一个问题,还是我疯了? - Greg
10个回答

21
我强烈建议只使用in_array(),任何速度差异都可以忽略不计,但是单独测试每个变量的可读性很差。 仅供娱乐,这是我运行的一个测试:
$array = array('test1', 'test2', 'test3', 'test4');
$var = 'test';
$iterations = 1000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') {}
}
$end = microtime(true);

print "Time1: ". ($end - $start)."<br />";

$start2 = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!in_array($var, $array) ) {}
}
$end2 = microtime(true);

print "Time2: ".($end2 - $start2)."<br />";

// Time1: 1.12536692619
// Time2: 1.57462596893

需要注意的是,如果$var没有设置,则方法1需要更长时间(这取决于您测试了多少条件)。

针对更新的PHP版本进行更新:

Martijn:我已将数组扩展到五个元素,并查找test3,作为一种平均情况。

PHP5.6

Time1: 0.20484399795532
Time2: 0.29854393005371

PHP7.1

Time1: 0.064045906066895
Time2: 0.056781053543091

PHP7.4

Time1: 0.048759937286377
Time2: 0.049691915512085

PHP8.0

Time1: 0.045055150985718
Time2: 0.049431085586548

结论:原始测试并不是最好的测试,而且在php7+中已经成为了一种偏好问题。


你使用的是哪个版本的PHP?5.2.6(Windows)的结果是Time1:1.36,Time2:4.88——这确实是一个很大的差异。 - Greg
2
你在一定程度上作弊了,因为你把 array(...) 从循环中拿出来了——如果你按照问题所说的那样做,我的 time2 就会超过6秒。 - Greg
将array()初始化移动到哪里算是“作弊”?循环与实际代码无关。如果假设所讨论的实际代码可能会被执行多次,那么OP当然会首先将变量设置为要搜索的项目数组。 - grantwparks
1
仅作为一个有趣的数据点,我在PHP 7.1.8上运行了这些代码。时间1:0.049320936203003,时间2:0.025889873504639。 - Chris Forrence
我的PHP 7.1时间 - 时间1:0.11170816421509,时间2:0.06308913230896。 - Nuno
显示剩余5条评论

10
请注意,如果您想替换一堆 !== 语句,应将第三个参数传递给 in_array 函数,并将其设置为 true,这将强制对数组中的项目进行类型检查。
显然,普通的 != 不需要这样做。

6

以下是与另一个案例相关的此工作台的实时更新 https://3v4l.org/OA2S7

PHP 7.3 的结果:

  • 多次对比: 0.057507991790771

  • in_array: 0.02568507194519

  • 循环外的 array_flip() + isset(): 0.014678001403809

  • 循环外未经测量的 array_flip() + isset(): 0.015650033950806

  • foreach 和比较: 0.062782049179077


6
第一种方法会更快 - 第二种方法有很多额外的开销:创建数组,调用函数,搜索数组...
然而,正如我在之前的一个问题中所说的,过早地进行优化是万恶之源。你应该编写易读的代码,然后如果需要优化,请对其进行分析,然后再进行优化。
编辑:
使用@Owen的代码进行测时(PHP 5.2.6 / windows):
Time1: 1.33601498604
Time2: 4.9349629879

将数组(...)移至循环内部,如问题所示:
Time1: 1.34736609459
Time2: 6.29464697838

抱歉打错了你的名字Owen! - Greg
如其他答案的评论所述,PHP7+已经进行了优化,因此这两种方法都只需要25-50毫秒,而不是1-6秒。 - Juha Untinen

4

嗨,我刚刚举了一个极端的例子,指出随着值数量的增加,普通比较不是最高效的方式。

这是我的代码:

$var = 'test';
$num_values = 1000;
$iterations = 1000000;
print "\nComparison performance test with ".$num_values." values and ".$iterations." loop iterations";
print "\n";

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if ($var != 'test0' &&
        $var != 'test1' &&
        // ...
        // yes I really have 1000 lines in my file
        // ...
        $var != 'test999') {}
}
print "\nCase 1: plain comparison";
print "\nTime 1: ". (microtime(true) - $start);
print "\n";

$start = microtime(true);
$array = array();
for($i=0; $i<$num_values; $i++) {
    $array1[] = 'test'.$i;
}
for($i = 0; $i < $iterations; ++$i) {
    if (!in_array($var, $array1) ) {}
}
print "\nCase 2: in_array comparison";
print "\nTime 2: ".(microtime(true) - $start);
print "\n";

$start = microtime(true);
$array = array();
for($i=0; $i<$num_values; $i++) {
    $array2['test'.$i] = 1;
}
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array2[$var])) {}
}
print "\nCase 3: values as keys, isset comparison";
print "\nTime 3: ".(microtime(true) - $start);
print "\n";

$start = microtime(true);
$array = array();
for($i=0; $i<$num_values; $i++) {
    $array3['test'.$i] = 1;
}
for($i = 0; $i < $iterations; ++$i) {
    if (!array_key_exists($var, $array3)) {}
}
print "\nCase 4: values as keys, array_key_exists comparison";
print "\nTime 4: ".(microtime(true) - $start);
print "\n";

我的结果(PHP 5.5.9):

Case 1: plain comparison
Time 1: 31.616894006729

Case 2: in_array comparison
Time 2: 23.226133823395

Case 3: values as keys, isset comparison
Time 3: 0.050863981246948

Case 4: values as keys, array_key_exists comparison
Time 4: 0.13700890541077

我同意,这可能有点极端,但它展示了PHP中类似哈希表的关联数组的大局和巨大潜力,你只需要好好利用它。


2
使用 in_array 函数处理大量数据时速度更快,但 "大量" 是非常主观的,取决于与数据和计算机相关的很多因素。因为您在询问,我假设您不是在处理少量项目。对于更长的列表,请注意这个信息,并使用翻转数组测量性能,以便 php 可以利用哈希查找而不是线性搜索。对于“静态”数组,该调整可能不会改善性能,但也可能会改善性能。
使用 Owen 的测试代码,使用翻转数组并进行更多迭代以获得更一致的结果:
$array2 = array_flip($array);
$iterations = 10000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array2[$var])) {}
}
$end = microtime(true);
print "Time3: ".($end - $start)."<br />";

Time1: 12.875
Time2: 13.7037701607
Time3: 3.70514011383

小注释:翻转一个大数组可能会使用大量内存。 - Darryl Hein
1
数组可以以翻转的配置声明。 - Sparr

1
当谈到PHP时,询问以下哪种方式更好:
  • 一组“if”和“else if”,
  • 一个带有一组“or”条件的“if”(如原帖中所述),或者
  • 使用“in_array”与即兴构建的数组,
应该记住,PHP语言的“switch”语句是设计用于这种情况的替代方案,可能是更好的答案。(尽管发布者的示例只涉及比较两个解决方案,但实际问题标题要求考虑in_array与PHP语句,因此我认为这是公平的)。在发布者的示例中,我会推荐:
switch ($var)
{ case 'test1': case 'test2': case 'test3': case 'test4':
     echo "We have a good value"; break;
  default:
     echo "We do not have a good value";
}

我希望PHP在case语句中允许一些非原始结构,例如用逗号表示“或”。但是以上就是PHP设计者认为处理这个问题最清晰的方式。而且在执行效率上似乎比其他两种选择更高效。
既然我在谈论愿望清单,SQL中的“IN”对于发布者的示例情况甚至更清晰。
这种想法可能导致人们想要使用“in_array”,针对这种情况,但不得不构建数据结构,然后使用为该数据结构设计的谓词,而不是有一种方法可以在没有发生这种开销的情况下直接说出它。

1
请注意,正如RoBorg所指出的那样,在创建数组时存在开销,因此应将其移动到迭代循环内部。因此,Sparr的帖子也有点误导性,因为使用array_flip函数也会产生开销。
以下是另一个包含所有5种变体的示例:
$array = array('test1', 'test2', 'test3', 'test4');
$var = 'test';
$iterations = 1000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
   if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') {}
}
print "Time1: ". (microtime(true) - $start);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
   if (!in_array($var, $array) ) {}
}
print "Time2: ".(microtime(true) - $start);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
   if (!in_array($var, array('test1', 'test2', 'test3', 'test4')) ) {}
}
print "Time2a: ".(microtime(true) - $start);

$array2 = array_flip($array);
$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
  if (!isset($array2[$var])) {}
}
print "Time3: ".(microtime(true) - $start);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    $array2 = array_flip($array);
  if (!isset($array2[$var])) {}
}
print "Time3a: ".(microtime(true) - $start);

我的结果:

Time1 : 0.59490108493 // straight comparison
Time2 : 0.83790588378 // array() outside loop - not accurate
Time2a: 2.16737604141 // array() inside loop
Time3 : 0.16908097267 // array_flip outside loop - not accurate
Time3a: 1.57209014893 // array_flip inside loop

总之,使用array_flip(与isset一起)比in_array更快,但不如直接比较快。

1
不要在循环中翻转数组。将其改为 $array2 = array('test1'=>0,'test2'=>0,'test3'=>0,'test4'=>0); 然后再试一次。在第一种情况下,数组是通过构造得到的,因此在这种情况下,你也应该构造数组。 - jmucchiello
在上面的评论中,isset将在0作为val时返回false,最好使用1作为val或使用array_key_exists。 - Jason

0

我知道这个问题已经有将近10年的历史了,但是还有其他的方法可以做到这一点。我使用了Nick的页面上的B方法,并且处理了成千上万条记录。速度非常快。

foreach(array_values($haystack) as $v)
    $new_haystack[$v] = 1; 
}

// So haystack becomes:
$arr[“String1”] = 1;
$arr[“String2”] = 1;
$arr[“String3”] = 1;


// Then check for the key:
if (isset($haystack[$needle])) {
    echo("needle ".$needle." found in haystack");
}

0

我的测试

$array = array('test1', 'test2', 'test3', 'test4');
$var = 'test';
$iterations = 1000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') {}
}
$end = microtime(true);

print "Time1: ". ($end - $start)."<br />";

$start2 = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!in_array($var, $array) ) {}
}
$end2 = microtime(true);

print "Time2: ".($end2 - $start2)."<br />";

$array_flip = array_flip($array);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array_flip[$var])) {}
}
$end = microtime(true);
print "Time3: ".($end - $start)."<br />";

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array[$var])) {}
}
$end = microtime(true);

print "Time4: ". ($end - $start)."<br />";

时间1:0.20001101493835 时间2:0.32601881027222 时间3:0.072004079818726 时间4:0.070003986358643

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