array_diff如何工作?

时间:2010-03-19 19:11:23

标签: php performance arrays

array_diff()如何运作?它显然不能如下工作:

function array_diff($arraya, $arrayb)
{
    $diffs = array();
    foreach ($arraya as $keya => $valuea)
    {
        $equaltag = 0;
        foreach ($arrayb as $valueb)     
        {
            if ($valuea == $valueb)
            {
                $equaltag =1;
                break;
            }
        }
        if ($equaltag == o)
        {
              $diffs[$keya]=$valuea;
        }

    }
    return $diffs;                          
}                                  //couldn't be worse than this

有谁知道更好的解决方案?

EDIT @animuson:

function array_diff($arraya, $arrayb)
{
    foreach ($arraya as $keya => $valuea)
    {
        if (in_array($valuea, $arrayb))
        {
            unset($arraya[$keya]);
        }
    }
    return $arraya;
}

6 个答案:

答案 0 :(得分:30)

user187291 建议通过哈希表在PHP中完成它非常棒!从这个幻想中汲取肾上腺素的冲动,我甚至找到了一种方法来加快它的速度(PHP 5.3.1):

function leo_array_diff($a, $b) {
    $map = array();
    foreach($a as $val) $map[$val] = 1;
    foreach($b as $val) unset($map[$val]);
    return array_keys($map);
}

基准来自user187291的帖子:

LEO=0.0322  leo_array_diff()
ME =0.1308  my_array_diff()
YOU=4.5051  your_array_diff()
PHP=45.7114 array_diff()

即使每个阵列有100个条目,array_diff()的性能滞后也很明显。

注意:此解决方案意味着第一个数组中的元素是唯一的(或者它们将变为唯一的)。这是散列解决方案的典型特征。

注意:解决方案不保留索引。将原始索引分配给$ map,最后使用array_flip()保存密钥。

function array_diff_pk($a, $b) {
    $map = array_flip($a);
    foreach($b as $val) unset($map[$val]);
    return array_flip($map);
}

PS:我在寻找一些array_diff()paradoxon时发现了这一点:如果在脚本中使用了两次,则array_diff()花费的时间要长三倍才能实现相同的任务。

答案 1 :(得分:23)

更新

  • see below代码更快/更好。

  • 在php 5.3.4中,array_diff行为要好得多,但仍然比Leo的功能慢10倍。

  • 还值得注意的是,这些函数并不完全等同于array_diff,因为它们不维护数组键,即my_array_diff(x,y) == array_values(array_diff(x,y))

/ UPDATE

更好的解决方案是使用hash maps

function my_array_diff($a, $b) {
    $map = $out = array();
    foreach($a as $val) $map[$val] = 1;
    foreach($b as $val) if(isset($map[$val])) $map[$val] = 0;
    foreach($map as $val => $ok) if($ok) $out[] = $val;
    return $out;
}

$a = array('A', 'B', 'C', 'D');
$b = array('X', 'C', 'A', 'Y');

print_r(my_array_diff($a, $b)); // B, D

基准

function your_array_diff($arraya, $arrayb)
{
    foreach ($arraya as $keya => $valuea)
    {
        if (in_array($valuea, $arrayb))
        {
            unset($arraya[$keya]);
        }
    }
    return $arraya;
}

$a = range(1, 10000);
$b = range(5000, 15000);

shuffle($a);
shuffle($b);

$ts = microtime(true);
my_array_diff($a, $b);
printf("ME =%.4f\n", microtime(true) - $ts);

$ts = microtime(true);
your_array_diff($a, $b);
printf("YOU=%.4f\n", microtime(true) - $ts);

结果

ME =0.0137
YOU=3.6282

有什么问题吗? ;)

而且,只是为了好玩,

$ts = microtime(true);
array_diff($a, $b);
printf("PHP=%.4f\n", microtime(true) - $ts);

结果

ME =0.0140
YOU=3.6706
PHP=19.5980

这太不可思议了!

答案 2 :(得分:7)

知道它是如何工作的最佳解决方案来看看它的源代码;-)
(嗯,这是开源的权力之一 - 如果你看到一些可能的优化,你可以提交一个补丁;-))

对于array_diff,它应该在ext/standard - 这意味着,对于PHP 5.3,它应该在那里:branches/PHP_5_3/ext/standard

然后,array.c文件看起来像一个看似合理的目标; php_array_diff函数第3381行似乎与array_diff对应。


(祝你好运通过代码:它很长......)

答案 3 :(得分:2)

似乎你可以通过使用另一个数组而不是取消设置来加快速度。但是,这会占用更多内存,这可能是一个问题,这取决于用例(我还没有测试过内存分配的实际差异)。

<?php
function my_array_diff($a, $b) {
  $map = $out = array();
  foreach($a as $val) $map[$val] = 1;
  foreach($b as $val) if(isset($map[$val])) $map[$val] = 0;
  foreach($map as $val => $ok) if($ok) $out[] = $val;
  return $out;
}
function leo_array_diff($a, $b) {
  $map = $out = array();
  foreach($a as $val) $map[$val] = 1;
  foreach($b as $val) unset($map[$val]);
  return array_keys($map);
}
function flip_array_diff_key($b, $a) {
  $at = array_flip($a);
  $bt = array_flip($b);
  $d = array_diff_key($bt, $at);
  return array_keys($d);
}
function flip_isset_diff($b, $a) {
  $at = array_flip($a);
  $d = array();
  foreach ($b as $i)
    if (!isset($at[$i]))
      $d[] = $i;
  return $d;
}
function large_array_diff($b, $a) {
  $at = array();
  foreach ($a as $i)
    $at[$i] = 1;
  $d = array();
  foreach ($b as $i)
    if (!isset($at[$i]))
      $d[] = $i;
  return $d;
}

$functions = array("flip_array_diff_key", "flip_isset_diff", "large_array_diff", "leo_array_diff", "my_array_diff", "array_diff");
#$functions = array_reverse($functions);
$l = range(1, 1000000);
$l2 = range(1, 1000000, 2);

foreach ($functions as $function) {
  $ts = microtime(true);
  for ($i = 0; $i < 10; $i++) {
    $f = $function($l, $l2);
  }
  $te = microtime(true);
  $timing[$function] = $te - $ts;
}
asort($timing);
print_r($timing);

我的时间是(PHP 5.3.27-1~dotdeb.0):

[flip_isset_diff] => 3.7415699958801
[flip_array_diff_key] => 4.2989008426666
[large_array_diff] => 4.7882599830627
[flip_flip_isset_diff] => 5.0816700458527
[leo_array_diff] => 11.086831092834
[my_array_diff] => 14.563184976578
[array_diff] => 99.379411935806

http://shiplu.mokadd.im/topics/performance-optimization/

找到了三个新功能

答案 4 :(得分:1)

由于这已经提出(参见@ BurninLeo的回答),这样的事情怎么样?

function binary_array_diff($a, $b) {
    $result = $a;
    asort($a);
    asort($b);
    list($bKey, $bVal) = each($b);
    foreach ( $a as $aKey => $aVal ) {
        while ( $aVal > $bVal ) {
            list($bKey, $bVal) = each($b);
        }
        if ( $aVal === $bVal ) {
            unset($result[$aKey]);
        }
    }
    return $result;
}

在进行一些测试后,结果似乎是可以接受的:

$a = range(1, 10000);
$b = range(5000, 15000);

shuffle($a);
shuffle($b);

$ts = microtime(true);
for ( $n = 0; $n < 10; ++$n ) {
    array_diff($a, $b);
}
printf("PHP    => %.4f\n", microtime(true) - $ts);

$ts = microtime(true);
for ( $n = 0; $n < 10; ++$n ) {
    binary_array_diff($a, $b);
}
printf("binary => %.4f\n", microtime(true) - $ts);

$binaryResult = binary_array_diff($a, $b);
$phpResult    = array_diff($a, $b);
if ( $binaryResult == $phpResult && array_keys($binaryResult) == array_keys($phpResult) ) {
    echo "returned arrays are the same\n";
}

输出:

PHP    => 1.3018
binary => 1.3601
returned arrays are the same

当然,PHP代码的性能不如C代码,因此毫无疑问PHP代码会慢一些。

答案 5 :(得分:-2)

来自PHP:“返回一个数组,其中包含array1中任何其他数组中不存在的所有条目。”

因此,您只需针对所有 arrayN 检查 array1 array1 中任何未出现在任何这些数组中的值都将是以新数组返回。

您甚至不需要遍历所有 array1 的值。仅针对所有其他数组,循环遍历其值并检查每个值是否为in_array($array1, $value)