array_unique 默认保留原始键名是设计行为,不是 bug;它只按值去重且不重索引,键名语义(如用户ID、配置项名)因此得以保留,多维数组或对象需手动序列化处理。

array_unique 默认保留原始键名是设计行为,不是 bug
PHP 的 array_unique 函数从设计上就明确“保留首次出现元素的键名”,这不是意外结果,而是其核心契约。它只按值去重,完全不碰键名结构——哪怕你传入的是索引数组(如 [0=>'a', 1=>'b', 2=>'a']),返回的也是 [0=>'a', 1=>'b'],键名照旧;传入关联数组(如 ['user_1'=>'x', 'user_2'=>'y', 'user_3'=>'x']),返回的也是 ['user_1'=>'x', 'user_2'=>'y']。
常见误解是以为“键名被重置了”,其实只是没做额外处理:它不自动调用 array_values(),也不强制重排索引。如果你看到输出里键名跳着走(比如 [0=>..., 5=>..., 9=>...]),那恰恰说明原数组就是这么稀疏的,array_unique 忠实还原了这个结构。
为什么 array_unique 不自动重索引?
因为 PHP 数组本质是有序哈希表,键名本身就是数据语义的一部分。强行重索引会破坏以下场景:
- 关联数组中键名代表业务标识(如用户 ID、配置项名),重索引后就丢失映射关系
- 前端渲染依赖键名生成 DOM ID 或表单 name 属性,重索引会导致 JS 绑定失效
- 后续逻辑用
isset($arr['status'])检查字段是否存在,重索引后键名消失即检查失败
换句话说:array_unique 的职责只有“去重”,不负责“整理”。是否需要连续数字索引,得由你显式决定——用 array_values(array_unique($arr)) 就行。
立即学习“PHP免费学习笔记(深入)”;
键名“还在”但顺序可能和预期不一致?
这是最易被忽略的细节:array_unique 内部会对值做字符串化比较((string) $val),并基于此排序后再取首个键。这意味着:
- 数值
1和字符串"1"被视为相同,去重后只留第一个出现的键,不管类型 - 如果数组里有
[0=>1, 1=>"1", 2=>2],结果是[0=>1, 2=>2](因为0键的1先出现) - 这种“按字符串值排序再选首键”的机制,可能导致键名顺序看起来“乱”,但其实是可预测的
若需严格按原始遍历顺序保留首个唯一值(不经过内部字符串排序),目前没有内置开关,得用自定义循环或 array_reduce 模拟。
多维数组或对象不能直接用 array_unique
array_unique 只支持一维、标量值数组。遇到以下情况会静默失败或报错:
- 数组含子数组:
array_unique([['a'], ['a']])返回原样,因子数组无法转成唯一字符串键 - 数组含对象:
array_unique([$obj1, $obj2])同样无效,对象比较需实现__toString或手动序列化 - 解决办法:先用
array_map('serialize', $arr)转成字符串数组,去重后再array_map('unserialize', ...),但要注意性能和安全性
真正复杂的去重逻辑,别硬套 array_unique——它轻量高效,但也只适合“值唯一性”这一层需求。键名要不要动、怎么动、多维怎么压平,都得你来拍板。



















