什么更快:in_array或isset?[关闭]


96

这个问题仅对我而言,因为我一直喜欢编写优化的代码,这些代码也可以在廉价的慢速服务器(或流量很大的服务器)上运行

我环顾四周,却找不到答案。我想知道在这两个示例之间有什么更快的方法,请记住在我的情况下数组的键并不重要(自然是伪代码):

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!in_array($new_val, $a){
        $a[] = $new_val;
        //do other stuff
    }
}
?>

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!isset($a[$new_val]){
        $a[$new_val] = true;
        //do other stuff
    }
}
?>

由于问题的关键不是数组冲突,所以我想补充一点,如果您担心冲突的插入$a[$new_value],可以使用$a[md5($new_value)]。它仍然可能导致冲突,但是当从用户提供的文件中读取内容时,它将避免可能的DoS攻击(http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html


3
如果您一直在努力编写优化的代码,那么您肯定会使用探查器,然后偶尔使用吗?
mario 2012年

59
我投票重启。该问题的格式正确,答案得到事实和参考的支持。虽然是优化,但这些类型的问题是建设性的
杰森·麦克莱里

5
@JasonMcCreary第二;还有一个。
2012年

7
这是很多年后的事,但我什至不认为这是微优化。对于大型数据集,它可以带来很大的不同!!
罗伯特

2
...这个问题对我来说“具有建设性”。我将开始另一个重新开放的广告系列。
mickmackusa

Answers:


117

到目前为止,答案是确定的。isset在这种情况下使用速度更快,因为

  • 它在键上使用O(1)哈希搜索,而in_array必须检查每个值,直到找到匹配项。
  • 作为操作码,它比调用in_array内置函数的开销少。

这些可以通过使用具有值的数组(在下面的测试中为10,000)来证明,这需要进行in_array更多的搜索。

isset:    0.009623
in_array: 1.738441

通过填写一些随机值并偶尔查找数组中存在的值,以此建立了Jason的基准。都是随机的,所以要注意时间会波动。

$a = array();
for ($i = 0; $i < 10000; ++$i) {
    $v = rand(1, 1000000);
    $a[$v] = $v;
}
echo "Size: ", count($a), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a[rand(1, 1000000)]);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array(rand(1, 1000000), $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

我知道哈希值,但想知道为什么在可能的情况下不对数组值执行类似的操作以加快功能,如果仅通过在值上添加额外的哈希来使用相似的值,也将减少内存消耗。
Fabrizio 2012年

3
@Fabrizio-数组值可以重复并且包含不可哈希对象。键必须是唯一的,并且只能是字符串和整数,这使得它们很容易散列。尽管您可以创建散列键和值的一对一映射,但这并不是PHP数组的工作方式。
David Harkness,2012年

3
如果您确定数组包含唯一值,则还有另一个选项-flip + isset
Arkadij Kuzhel 2015年

值得一提的是,在本示例中,翻转的isset仍然比in_array更快:```$ start = microtime(true); $ foo = array_flip($ a); for($ i = 0; $ i <10000; ++ $ i){isset($ foo [rand(1,1000000)]); } $ total_time = microtime(true)-$ start; echo“总时间(翻转isset):”,number_format($ total_time,6),PHP_EOL;
安德烈·鲍米耶

@AndreBaumeier哪个更快,将取决于数组的大小以及要进行的测试数量。翻转一万个元素数组以执行三个测试可能没有效率。
David Harkness

42

哪个更快:isset()vsin_array()

isset() 是比较快的。

显而易见,isset()仅测试单个值。鉴于in_array()将遍历整个数组,测试每个元素的值。

粗略的基准测试很容易使用microtime()

结果:

Total time isset():    0.002857
Total time in_array(): 0.017103

注意:无论是否存在,结果都是相似的。

码:

<?php
$a = array();
$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a['key']);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array('key', $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

exit;

其他资源

我鼓励您也看一下:


不错的解决方案。令我惊讶的是,越来越多的人没有花更多时间使用microtime()其他工具或功能。不可思议的价值。
nickhar 2012年

1
在空数组中搜索相同的键只会突出显示调用in_array函数和使用isset内置函数的开销。对于包含一堆随机键并偶尔搜索现有键/值的数组,这会更好。
David Harkness,2012年

我使用的基准和microtime中颇有几分,但是我也知道,虽然我是测试while,并foreach在每个刷新我得到不同的“赢家”。它总是取决于太多的服务器变量,最好的方法是在不同的时间进行大量的迭代,并获得更频繁获胜的结果,或者只是知道后台发生了什么并知道它将是最终的赢家无论如何
Fabrizio 2012年

@David Harkness,您已经挑剔了我的答案。如果您想要更多,请站在我的肩膀上并发表您自己的答案。:)尽管如此,如果相对于功能开销而言,其开销已经大大增加了isset(),那么您认为传递给它更大的数组会更快呢?
杰森·麦克雷里


19

使用isset()可以提高查找速度,因为它使用哈希表,从而避免了O(n)搜索。

首先使用djb哈希函数对密钥进行哈希处理,以确定中类似哈希密钥的存储桶O(1)。然后反复搜索该存储桶,直到在中找到确切的密钥O(n)

除非有任何有意的散列冲突,否则此方法的性能要优于in_array()

请注意,isset()按照显示的方式使用时,将最终值传递给另一个函数需要使用array_keys()创建一个新数组。通过将数据存储在键和值中,可能会造成内存折衷。

更新资料

查看代码设计决策如何影响运行时性能的好方法,可以查看脚本的编译版本

echo isset($arr[123])

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   ZEND_ISSET_ISEMPTY_DIM_OBJ              2000000  ~0      !0, 123
         1      ECHO                                                 ~0
         2    > RETURN                                               null

echo in_array(123, $arr)

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   SEND_VAL                                             123
         1      SEND_VAR                                             !0
         2      DO_FCALL                                 2  $0      'in_array'
         3      ECHO                                                 $0
         4    > RETURN                                               null

不仅in_array()使用效率相对较低的O(n)搜索,还需要将其称为函数(DO_FCALL),而为此isset()使用单个操作码(ZEND_ISSET_ISEMPTY_DIM_OBJ)。


7

第二个会更快,因为它只查找特定的数组键,不需要遍历整个数组,直到找到为止(如果找不到,将查看每个数组元素)


但也取决于在全球范围内搜索到的var的下落
el Dude 2012年

@ EL2002,能否请您详细说明一下?
Fabrizio 2012年

1
迈克,即使找不到,也不会查看整个数组isset()吗?
Fabrizio 2012年

1
@Fabrizio不,它不需要迭代。在内部(在C中),PHP数组只是一个哈希表。为了查找单个索引值,C只是对该值进行散列并查找其在内存中的分配位置。有或没有价值。
Mike Brant 2012年

1
@Fabrizio本文很好地概述了PHP如何在C中内部表示数组。 nikic.github.com/2012/03/28/…–
Mike Brant
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.