獲取「存在於數組 A 但不存在於數組 B」的元素是常見需求。本文對比原生函數、手寫循環、鍵值差集等多種實現,分析性能與適用場景。
$a = [1, 2, 3, 4, 5, 'a', 'b']; $b = [3, 4, 'b', 'c']; // 預期結果:[1, 2, 5, 'a'](保持 A 的順序與鍵名) // 注意:是否保留鍵名、是否嚴格比較、多維數組如何處理
<?php $result = array_diff($a, $b); // 結果:[0=>1, 1=>2, 4=>5, 5=>'a'](保留原鍵名,重新索引需 array_values()) // 多數組差集 $result = array_diff($a, $b, $c, $d); // 關鍵特性 // - 比較值(字符串比較,'1' == 1 為 true) // - 保留 A 的鍵名 // - 一維數組專用 ?>
<?php
// 嚴格比較(類型+值)
$result = array_udiff($a, $b, fn($x, $y) => $x <=> $y); // PHP 7+
// 或
$result = array_udiff($a, $b, function($x, $y) {
return ($x === $y) ? 0 : (($x > $y) ? 1 : -1);
});
// 對象/多維數組比較
$objectsA = [['id'=>1], ['id'=>2]];
$objectsB = [['id'=>2]];
$result = array_udiff($objectsA, $objectsB, function($x, $y) {
return $x['id'] <=> $y['id'];
});
// 結果:[['id'=>1]]
?>
<?php $a = ['x'=>1, 'y'=>2, 'z'=>3]; $b = ['y'=>99, 'w'=>88]; // 按鍵名差集(忽略值) $result = array_diff_key($a, $b); // ['x'=>1, 'z'=>3] // 自定義鍵比較 $result = array_diff_ukey($a, $b, 'strcasecmp'); // 大小寫不敏感鍵比較 ?>方法四:手寫循環(性能優化、特殊邏輯)
<?php /** * 高性能差集:利用鍵值查找 O(1) 特性 * 適用於大數據量、頻繁調用 */ function arrayDiffFast(array $a, array $b): array { // 將 B 轉為鍵值映射(值作為鍵) $bMap = array_flip($b); // 值必須能作為鍵(int/string) $result = []; foreach ($a as $key => $value) { if (!isset($bMap[$value])) { $result[$key] = $value; // 保留原鍵名 } } return $result; } /** * 嚴格模式差集(類型+值) */ function arrayDiffStrict(array $a, array $b): array { $bMap = []; foreach ($b as $v) { // 序列化區分類型:1 vs '1' $bMap[serialize($v)] = true; } $result = []; foreach ($a as $key => $value) { if (!isset($bMap[serialize($value)])) { $result[$key] = $value; } } return $result; } ?>方法五:多維數組差集(遞歸)
<?php function arrayDiffRecursive(array $a, array $b): array { $result = []; foreach ($a as $key => $value) { if (!array_key_exists($key, $b)) { $result[$key] = $value; } elseif (is_array($value) && is_array($b[$key])) { $diff = arrayDiffRecursive($value, $b[$key]); if ($diff) $result[$key] = $diff; } elseif ($value !== $b[$key]) { $result[$key] = $value; } } return $result; } // 示例 $a = ['user' => ['id'=>1, 'tags'=>['a','b']], 'status'=>1]; $b = ['user' => ['id'=>1, 'tags'=>['b']], 'status'=>2]; print_r(arrayDiffRecursive($a, $b)); // ['user' => ['tags'=>['a']], 'status'=>1] ?>性能基準測試(10萬元素)
<?php $a = range(1, 100000); $b = range(50000, 150000); $start = microtime(true); $r1 = array_diff($a, $b); echo "array_diff: " . (microtime(true) - $start) * 1000 . " ms\n"; // ~15ms $start = microtime(true); $r2 = arrayDiffFast($a, $b); echo "arrayDiffFast: " . (microtime(true) - $start) * 1000 . " ms\n"; // ~5ms $start = microtime(true); $bMap = array_flip($b); $r3 = []; foreach ($a as $k=>$v) if(!isset($bMap[$v])) $r3[$k]=$v; echo "inline flip: " . (microtime(true) - $start) * 1000 . " ms\n"; // ~4ms ?>對比總結表
| 方法 | 比較方式 | 保留鍵名 | 多維支援 | 性能 | 推薦場景 |
|---|---|---|---|---|---|
| array_diff | 鬆散 (==) | 是 | 否 | 中 | 通用一維數組 |
| array_udiff | 自定義 | 是 | 是(遞歸) | 慢 | 對象、複雜比較 |
| array_diff_key | 鍵名 | 是 | 否 | 快 | 鍵基差集 |
| array_flip+isset | 鬆散 | 可選 | 否 | 最快 | 大數據、高頻 |
| serialize+isset | 嚴格 (===) | 可選 | 否 | 快 | 需區分類型 |
| 遞歸自寫 | 自定義 | 是 | 是 | 中 | 多維結構差異 |
根據數據規模、嚴格程度、維度選擇合適方法,`array_flip + isset` 是高性能通用首選。