電商網站常見需求:用戶依多個規格(品牌、價格、規格)篩選商品。資料庫通常採用「縱表」設計(每個規格一筆記錄),應用層需對多個 ID 陣列取交集。假設每個規格回傳約 1,000 個唯一 ID,如何最高效計算交集?
<?php
$rand = function() {
$result = [];
for ($i = 0; $i < 1000; ) {
$value = mt_rand(1, 10000);
if (!isset($result[$value])) {
$result[$value] = null;
$i++;
}
}
return array_keys($result);
};
$param_a = $rand();
$param_b = $rand();
?>
array_intersect()<?php
$time = microtime(true);
$result = array_intersect($param_a, $param_b);
$time = microtime(true) - $time;
echo "array_intersect: {$time}s\n";
// 輸出範例:0.0239 秒
?>
<?php
function intersect() {
if (func_num_args() < 2) trigger_error('param error', E_USER_ERROR);
$args = func_get_args();
foreach ($args as $arg) if (!is_array($arg)) trigger_error('param error', E_USER_ERROR);
$intersect = function($a, $b) {
$result = [];
$i = $j = 0;
$len_a = count($a); $len_b = count($b);
while ($i < $len_a && $j < $len_b) {
if ($a[$i] < $b[$j]) $i++;
elseif ($a[$i] > $b[$j]) $j++;
else { $result[] = $a[$i]; $i++; $j++; }
}
return $result;
};
$result = array_shift($args);
sort($result);
foreach ($args as $arg) {
sort($arg);
$result = $intersect($result, $arg);
}
return $result;
}
$time = microtime(true);
$result = intersect($param_a, $param_b);
$time = microtime(true) - $time;
echo "intersect: {$time}s\n";
// 輸出範例:0.0026 秒(快約 9 倍)
?>
| 特性 | array_intersect | 自訂 intersect |
|---|---|---|
| 效能 | 較慢 | 極快(排序後 O(N)) |
| 重複值處理 | 保留第一陣列所有重複值 | 自動去重(集合語意) |
| 鍵名保留 | 保留原鍵名 | 重新索引(0-based) |
| 適用場景 | 需保留重複值/鍵名 | 大量唯一 ID 交集、效能優先 |
array_count_values + array_mergefunction intersect_simple() {
$args = func_get_args();
$data = array_count_values(call_user_func_array('array_merge', $args));
$result = [];
foreach ($data as $val => $cnt) if ($cnt > 1) $result[] = $val;
return $result;
}
程式碼簡潔,但 array_merge 會建立巨大臨時陣列,記憶體消耗大,適合資料量較小場景。
處理大量唯一 ID 交集時,先排序再雙指標掃描 的自訂演算法效能遠勝內建函式。但需確認業務邏輯是否要求保留重複值或鍵名,再決定採用哪種方案。