105

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

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

<?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)]. 它仍然可能导致冲突,但在从用户提供的文件(http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html)读取时可以避免可能的 DoS 攻击

4

4 回答 4

128

到目前为止的答案是准确的。在这种情况下使用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;
于 2012-11-20T22:48:14.670 回答
46

哪个更快: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;

其他资源

我鼓励你也看看:

于 2012-11-20T22:27:34.760 回答
19

Usingisset()利用了更快的查找,因为它使用哈希表,避免了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)。

于 2012-11-20T23:09:04.820 回答
7

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

于 2012-11-20T22:22:45.487 回答