二維陣列按某字段排序是高頻需求。本文對比自定義插入排序、usort、array_multisort、array_column + array_multisort 等多種方案的性能、代碼量與適用場景。
$data = [
['id' => 1, 'price' => 19, 'name' => 'A'],
['id' => 2, 'price' => 121, 'name' => 'B'],
['id' => 3, 'price' => 115, 'name' => 'C'],
['id' => 4, 'price' => 113, 'name' => 'D'],
['id' => 5, 'price' => 112, 'name' => 'E'],
];
// 升序
$prices = array_column($data, 'price');
array_multisort($prices, SORT_ASC, $data);
// 降序
array_multisort($prices, SORT_DESC, $data);
// 多字段:先價格升序,再 ID 降序
$prices = array_column($data, 'price');
$ids = array_column($data, 'id');
array_multisort($prices, SORT_ASC, $ids, SORT_DESC, $data);
// 升序
usort($data, fn($a, $b) => $a['price'] <=> $b['price']);
// 降序
usort($data, fn($a, $b) => $b['price'] <=> $a['price']);
// 多字段
usort($data, function($a, $b) {
$cmp = $a['price'] <=> $b['price'];
return $cmp !== 0 ? $cmp : $b['id'] <=> $a['id'];
});
<=> 極其優雅;array_multisort 慢(用戶態函數調用開銷)。function sort_insert(array &$arr, string $field, bool $asc = true): void {
for ($i = 1; $i < count($arr); $i++) {
$key = $arr[$i];
$j = $i - 1;
while ($j >= 0 && (($asc ? $key[$field] < $arr[$j][$field] : $key[$field] > $arr[$j][$field]))) {
$arr[$j + 1] = $arr[$j];
$j--;
}
$arr[$j + 1] = $key;
}
}
class PriceHeap extends SplHeap {
protected function compare($a, $b) {
return $a['price'] <=> $b['price']; // 最小堆
}
}
$heap = new PriceHeap();
foreach ($data as $row) $heap->insert($row);
$sorted = [];
foreach ($heap as $row) $sorted[] = $row;
// $sorted 為價格升序
| 方法 | 耗時 (ms) | 記憶體 (MB) | 備註 |
|---|---|---|---|
| array_multisort + array_column | ~18 | ~12 | 最快,C 層優化 |
| usort (spaceship) | ~45 | ~14 | 次之,靈活 |
| usort (closure 老寫法) | ~62 | ~15 | 避免創建閉包開銷 |
| SplHeap | ~55 | ~8 | 低記憶體,適合流式 |
| 插入排序 | >30000 | ~20 | 不可用 |
| 問題 | 原因 | 修正 |
|---|---|---|
| 字段值為字串數字('100') | 字串比較 '100' < '20' | 轉型:(int)$a['price'] 或 $a['price'] + 0 |
| 字段不存在 | Notice/Warning | $a['price'] ?? 0 或預處理補全 |
| 需保持鍵名關聯 | usort/array_multisort 重置數值鍵 | 用 uasort 或排序前記錄鍵 |
| 多字段排序邏輯反 | 優先級順序寫反 | array_multisort 參數順序 = 優先級順序 |
array_multisort + array_column:性能最好、代碼最少;usort + <=>:可讀性強、支援任意比較;SplHeap:記憶體友好;array_walk($data, fn(&$v) => $v['price'] = (float)$v['price']);function array_sort_by_field(array &$arr, string $field, int $order = SORT_ASC, string $type = SORT_NUMERIC): void {
$vals = array_column($arr, $field);
array_multisort($vals, $order, $type, $arr);
}
// 多字段
function array_sort_multi(array &$arr, array $fields): void {
$args = [];
foreach ($fields as $field => $config) {
$args[] = array_column($arr, $field);
$args[] = $config['order'] ?? SORT_ASC;
$args[] = $config['type'] ?? SORT_NUMERIC;
}
$args[] = &$arr;
array_multisort(...$args);
}
// 使用
array_sort_multi($data, [
'price' => ['order' => SORT_ASC],
'id' => ['order' => SORT_DESC]
]);
掌握這幾種核心方案,能應對 99% 的二維陣列排序場景。