最可靠的方法是用 array_unique() 配合 serialize():先 array_map('serialize', $arr) 将每行转字符串,再 array_unique() 去重,最后 array_map('unserialize', ...) 还原;避免用 json_encode() 或 SORT_REGULAR(不稳定)。

用 array_unique() 配合 SERIALIZE 是最常用且可靠的做法
PHP 原生的 array_unique() 默认只支持一维数组去重,对二维数组直接调用会报错或返回空/不生效。真正能按“整行”(即子数组完全相同)去重的方式,是先把每行转成可比较的标量,再用 array_unique() 处理。
最稳妥的是用 serialize():它能把任意嵌套结构转成唯一字符串,且保持键名、类型、顺序信息,反序列化后也能还原(虽然这里不需要还原)。注意别用 json_encode() —— 它会丢键名(转成对象)、对 null/false 处理不一致,还受编码影响。
实操建议:
- 先用
array_map('serialize', $arr)把二维数组每行转成字符串 - 再用
array_unique()去重,得到去重后的字符串数组 - 最后用
array_map('unserialize', ...)变回二维数组 - 如果原始键名重要(比如要保留数字索引从 0 开始),加一步
array_values()
示例:
立即学习“PHP免费学习笔记(深入)”;
$arr = [
['name' => 'Alice', 'age' => 25],
['name' => 'Bob', 'age' => 30],
['name' => 'Alice', 'age' => 25], // 重复行
];
$unique = array_values(
array_map('unserialize',
array_unique(array_map('serialize', $arr))
)
);
为什么不能直接用 array_unique($arr, SORT_REGULAR)
PHP 7.2+ 确实给 array_unique() 加了第二个参数 SORT_REGULAR,但它在二维数组上表现不稳定:某些版本会警告 “Array to string conversion”,有些则静默失败,返回原数组或部分去重结果。根本原因是 PHP 内部比较逻辑没为嵌套数组设计完整相等判断,尤其涉及浮点、null、引用或对象时极易出错。
容易踩的坑:
- 文档里写支持
SORT_REGULAR,但实际二维数组场景不可信 - 即使某次运行看似成功,升级 PHP 小版本后可能突然失效
- 错误不抛异常,只返回意外结果,难调试
需要保留原始键名?那就别用 array_values()
上面示例用了 array_values() 是为了重排索引,让结果是连续数字键。如果你依赖原始键(比如 [10] => [...], [25] => [...]),或者子数组里有非数字键(如 'user_123')且必须保留,就跳过这步。
但要注意:array_unique() 在处理 serialize 后的字符串时,**默认保留第一个出现的键名**,所以去重后键名不会乱,只是可能不连续。如果后续要 foreach 遍历且不关心键,基本无影响;如果要用 for ($i=0; $i<count($unique); $i++),就必须用 array_values()。
大数据量时性能明显下降,得提前想好替代方案
serialize() + unserialize() 每行都要做两次序列化操作,当二维数组超过几千行、每行字段又多时,内存和 CPU 开销会明显上升。不是不能用,而是得意识到这个成本。
可考虑的折中方式:
- 如果所有子数组结构固定(比如都是
['id', 'name', 'email']),改用拼接关键字段:$row['id']. '|' . $row['email'],再用这个字符串去重 —— 快很多,但需确保分隔符不会出现在数据里 - 如果数据来自数据库,优先在 SQL 层用
DISTINCT或GROUP BY去重,避免把冗余数据传进 PHP - 实在要 PHP 处理且量大,考虑用
foreach手写哈希表:用serialize($row)当 key,跳过已存在的行 —— 逻辑清晰,也比全量array_map稍省内存
序列化去重本身没问题,但别把它当成黑盒工具——哪一行重复、为什么重复、要不要保留元信息,这些才是实际项目里更常卡住的地方。



















