Skip to content

排序函数

概述

PHP 提供了丰富的排序函数,可以对数组按值、按键、按自定义规则进行排序。排序函数分为两大类:会重建索引的和保持键值关联的。本章详细讲解各种排序函数及其标志选项。

基础概念

排序函数分类

函数排序依据保持键重建索引
sort值升序
rsort值降序
asort值升序
arsort值降序
ksort键升序
krsort键降序
usort自定义值升序
uasort自定义值升序
uksort自定义键升序
natsort自然排序
natcasesort自然排序(不区分大小写)
array_multisort多维/多列排序

语法与代码

sort / rsort — 值排序(重建索引)

php
<?php

declare(strict_types=1);

// sort - 升序排列,重建数字索引
$fruits = ['banana', 'apple', 'cherry', 'date'];
sort($fruits);
// ['apple', 'banana', 'cherry', 'date']

// rsort - 降序排列,重建数字索引
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];
rsort($numbers);
// [9, 6, 5, 4, 3, 2, 1, 1]

// 注意:sort/rsort 会丢失键名
$assoc = ['name' => 'Alice', 'age' => 30];
sort($assoc);
// [30, 'Alice'](键变成了 0, 1)

asort / arsort — 值排序(保持键)

php
<?php

declare(strict_types=1);

// asort - 按值升序,保持键值关联
$prices = ['apple' => 3, 'banana' => 2, 'cherry' => 5];
asort($prices);
// ['banana' => 2, 'apple' => 3, 'cherry' => 5]

// arsort - 按值降序,保持键值关联
arsort($prices);
// ['cherry' => 5, 'apple' => 3, 'banana' => 2]

ksort / krsort — 键排序

php
<?php

declare(strict_types=1);

// ksort - 按键升序
$data = ['c' => 3, 'a' => 1, 'b' => 2];
ksort($data);
// ['a' => 1, 'b' => 2, 'c' => 3]

// krsort - 按键降序
krsort($data);
// ['c' => 3, 'b' => 2, 'a' => 1]

排序标志

php
<?php

declare(strict_types=1);

$fruits = ['Banana', 'apple', 'Cherry', 'avocado'];

// SORT_REGULAR - 默认(区分大小写)
sort($fruits, SORT_REGULAR);
// ['Banana', 'Cherry', 'apple', 'avocado'](大写字母排前面)

// SORT_STRING - 字符串排序
sort($fruits, SORT_STRING);
// 同上

// SORT_STRING | SORT_FLAG_CASE - 不区分大小写
sort($fruits, SORT_STRING | SORT_FLAG_CASE);
// ['apple', 'avocado', 'Banana', 'Cherry']

// SORT_NUMERIC - 数字排序
$mixed = ['10', '2', '1', '20'];
sort($mixed, SORT_NUMERIC);
// ['1', '2', '10', '20']

// SORT_NATURAL - 自然排序
$files = ['img1.png', 'img10.png', 'img2.png', 'img20.png'];
sort($files, SORT_NATURAL);
// ['img1.png', 'img2.png', 'img10.png', 'img20.png']

// SORT_NATURAL | SORT_FLAG_CASE
natsort($files);
sort($files, SORT_NATURAL | SORT_FLAG_CASE);

usort / uasort / uksort — 自定义排序

php
<?php

declare(strict_types=1);

// usort - 自定义值排序(重建索引)
$employees = [
    ['name' => 'Alice', 'age' => 30],
    ['name' => 'Bob', 'age' => 25],
    ['name' => 'Charlie', 'age' => 35],
];

// 按年龄升序
usort($employees, fn(array $a, array $b): int => $a['age'] <=> $b['age']);

// 按姓名降序
usort($employees, fn(array $a, array $b): int => $b['name'] <=> $a['name']);

// uasort - 自定义值排序(保持键)
$scores = ['Alice' => 85, 'Bob' => 92, 'Charlie' => 78];
uasort($scores, fn(int $a, int $b): int => $b <=> $a);
// ['Bob' => 92, 'Alice' => 85, 'Charlie' => 78]

// uksort - 自定义键排序
$data = ['item3' => 3, 'item1' => 1, 'item2' => 2, 'item10' => 10];
uksort($data, fn(string $a, string $b): int => strnatcmp($a, $b));
// ['item1' => 1, 'item2' => 2, 'item3' => 3, 'item10' => 10]

natsort / natcasesort — 自然排序

php
<?php

declare(strict_types=1);

// natsort - 自然顺序排序(数字部分按数值排)
$files = ['file1.txt', 'file10.txt', 'file2.txt', 'file20.txt'];
natsort($files);
// ['file1.txt', 'file2.txt', 'file10.txt', 'file20.txt']

// natcasesort - 不区分大小写的自然排序
$images = ['IMG1.jpg', 'img10.jpg', 'img2.jpg', 'Img20.jpg'];
natcasesort($images);
// ['IMG1.jpg', 'img2.jpg', 'img10.jpg', 'Img20.jpg']

array_multisort — 多列排序

php
<?php

declare(strict_types=1);

// array_multisort - 多维数组排序
$employees = [
    ['name' => 'Alice', 'department' => 'eng', 'salary' => 5000],
    ['name' => 'Bob', 'department' => 'eng', 'salary' => 3000],
    ['name' => 'Charlie', 'department' => 'mkt', 'salary' => 4000],
    ['name' => 'Diana', 'department' => 'mkt', 'salary' => 6000],
];

// 提取排序列
$departments = array_column($employees, 'department');
$salaries = array_column($employees, 'salary');

// 先按部门升序,同部门按薪资降序
array_multisort($departments, SORT_ASC, $salaries, SORT_DESC, $employees);

print_r($employees);
// eng: Bob(3000), Alice(5000)
// mkt: Charlie(4000), Diana(6000)

详细说明

飞船运算符 <=>

PHP 7.0+ 引入的飞船运算符是编写排序回调的最佳工具:

php
<?php

declare(strict_types=1);

// $a <=> $b
// $a < $b  返回 -1
// $a == $b 返回 0
// $a > $b  返回 1

// 升序: $a <=> $b
// 降序: $b <=> $a

选择建议

关联数组使用 asort/arsort/ksort(保持键),索引数组使用 sort/rsort/usort。自定义排序优先使用 usort + 飞船运算符。

实战示例

商品多条件排序

php
<?php

declare(strict_types=1);

$products = [
    ['name' => 'A', 'price' => 100, 'sales' => 500],
    ['name' => 'B', 'price' => 50, 'sales' => 1000],
    ['name' => 'C', 'price' => 100, 'sales' => 300],
    ['name' => 'D', 'price' => 50, 'sales' => 800],
];

// 先按价格升序,同价按销量降序
usort($products, function (array $a, array $b): int {
    $cmp = $a['price'] <=> $b['price'];
    return $cmp !== 0 ? $cmp : ($b['sales'] <=> $a['sales']);
});

// 结果: B(50,1000), D(50,800), A(100,500), C(100,300)

多维数组排序模式

php
<?php

declare(strict_types=1);

// 多维数组排序的常用模式

// 按 sub-array 的某个字段排序
$employees = [
    ['name' => 'Alice', 'dept' => 'eng', 'salary' => 5000],
    ['name' => 'Bob', 'dept' => 'eng', 'salary' => 7000],
    ['name' => 'Charlie', 'dept' => 'mkt', 'salary' => 4000],
    ['name' => 'Diana', 'dept' => 'mkt', 'salary' => 6000],
];

// 按 salary 降序
usort($employees, fn(array $a, array $b): int => $b['salary'] <=> $a['salary']);

// 按 dept 升序,同 dept 按 salary 降序
usort($employees, function (array $a, array $b): int {
    $cmp = $a['dept'] <=> $b['dept'];
    if ($cmp !== 0) return $cmp;
    return $b['salary'] <=> $a['salary'];
});

// 封装为通用排序函数
function sortByField(array &$array, string $field, bool $desc = false): void
{
    usort($array, function (array $a, array $b) use ($field, $desc): int {
        $cmp = $a[$field] <=> $b[$field];
        return $desc ? -$cmp : $cmp;
    });
}

function sortByFields(array &$array, array $fields): void
{
    usort($array, function (array $a, array $b) use ($fields): int {
        foreach ($fields as $field => $direction) {
            $cmp = $a[$field] <=> $b[$field];
            if ($cmp !== 0) {
                return $direction === 'desc' ? -$cmp : $cmp;
            }
        }
        return 0;
    });
}

sortByFields($employees, ['dept' => 'asc', 'salary' => 'desc']);

对象数组排序

php
<?php

declare(strict_types=1);

class Student
{
    public function __construct(
        public string $name,
        public int $score,
        public string $grade
    ) {}
}

$students = [
    new Student('Alice', 95, 'A'),
    new Student('Bob', 82, 'B'),
    new Student('Charlie', 78, 'C'),
    new Student('Diana', 92, 'A'),
];

// 按成绩降序
usort($students, fn(Student $a, Student $b): int => $b->score <=> $a->score);

// 按成绩降序,同分按姓名升序
usort($students, function (Student $a, Student $b): int {
    $cmp = $b->score <=> $a->score;
    return $cmp !== 0 ? $cmp : $a->name <=> $b->name;
});

foreach ($students as $s) {
    echo "{$s->name}: {$s->score}\n";
}
// Diana: 92
// Alice: 95
// Bob: 82
// Charlie: 78

排序稳定性

php
<?php

declare(strict_types=1);

// PHP 排序不是稳定排序(等值元素的原始顺序可能改变)
// 如需稳定排序,需要在比较函数中加入原始索引

function stableSort(array $array, callable $comparator): array
{
    $indexed = [];
    foreach ($array as $index => $value) {
        $indexed[] = ['index' => $index, 'value' => $value];
    }

    usort($indexed, function (array $a, array $b) use ($comparator): int {
        $cmp = $comparator($a['value'], $b['value']);
        return $cmp !== 0 ? $cmp : ($a['index'] <=> $b['index']);
    });

    return array_column($indexed, 'value');
}

// 使用示例
$data = [
    ['name' => 'A', 'score' => 90],
    ['name' => 'B', 'score' => 85],
    ['name' => 'C', 'score' => 90],
    ['name' => 'D', 'score' => 85],
];

$sorted = stableSort($data, fn(array $a, array $b): int => $a['score'] <=> $b['score']);
// A 在 C 前面,B 在 D 前面(原始顺序保持)

PHP 排序实现

PHP 使用快速排序(quick sort)变体实现排序函数。PHP 8.0+ 优化了排序算法的性能。对于大数据集,排序时间复杂度为 O(n log n)。

注意事项

sort 会重建索引

sortrsort 会丢弃原来的键名,重建为数字索引。关联数组排序请使用 asort/arsort

排序标志

SORT_FLAG_CASE 必须与 SORT_STRINGSORT_NATURAL 一起使用,不能单独使用。

最佳实践

  1. 关联数组用 asort/arsort:保持键值关联
  2. 自定义排序用 usort + <=>:简洁高效
  3. 文件名排序用 natsort:正确的数字排序
  4. 多列排序用 array_multisort:配合 array_column
  5. 大小写不敏感用 SORT_FLAG_CASE

参考链接