array_unique 不重置索引,它保留首次出现元素的原始键名;需连续索引时须显式调用 array_values()。

array_unique 本来就不重置索引
array_unique 的设计行为就是保留原始键名,不是“为什么还在”,而是它压根不会动键名。它只做一件事:遍历数组,对每个值做哈希比对,遇到重复值时跳过后续出现项,但首次出现的那个键名原封不动留下。
常见误解是以为去重后该变成 [0] => apple, [1] => banana 这种连续索引 —— 那其实是 array_values() 干的活,不是 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'] - 哪怕原始键不连续(如
[5=>'a', 10=>'b', 15=>'a']),结果也是[5=>'a', 10=>'b'],不会压缩或重排
什么时候会看到“键名不见了”?
真正让人困惑的场景,往往不是键名被删了,而是你用了 foreach 或 json_encode 后发现顺序乱了、或者前端收不到预期 key —— 这通常是因为:
- 你误把
array_unique和array_values混用了,比如写了array_values(array_unique($arr)),那当然键名全变0,1,2... - 你用的是多维数组,而
array_unique对子数组无效,返回空或报错,导致你以为“键没了”,其实是整个元素被丢弃了 - 你在调试时用了
print_r看输出,但没注意键名其实还在 —— 它只是没按数字顺序排列,容易被忽略
SORT_STRING 导致的隐式类型转换陷阱
默认情况下 array_unique 用 SORT_STRING 比较,会把所有值转成字符串再判等。这意味着 1 和 '1' 被当成相同值,去重时只留第一个 —— 键名当然也跟着第一个走。
立即学习“PHP免费学习笔记(深入)”;
例如:
$arr = [0 => 1, 1 => '1', 2 => 2]; $result = array_unique($arr); // 结果是 [0 => 1, 2 => 2],键 1 被丢弃,不是函数出错,是值判等逻辑使然
- 想避免这种类型混同,显式传
SORT_REGULAR:array_unique($arr, SORT_REGULAR) -
SORT_NUMERIC适合纯数字比较,但会把'01'当作1,仍可能误判 - 若需严格区分类型和值,得自己写循环 +
spl_object_hash或序列化判断
需要连续索引时,必须手动调用 array_values
如果你的下游逻辑依赖 $arr[0]、$arr[1] 这种访问方式,或者要 json_encode 成标准 JSON 数组(而非对象),那就不能只靠 array_unique。
正确做法是两步明确分离:
- 先去重:
$unique = array_unique($arr) - 再重索引:
$final = array_values($unique)
这一步不能省,也没有替代函数 —— PHP 不会在去重时自动帮你做索引归一化,因为“是否需要连续索引”本身就是业务决策,不是通用规则。
最容易被忽略的是:这个两步操作不可逆。一旦调用 array_values,原始键名就彻底丢失了。如果之后还要回溯来源(比如日志里要标出是哪条记录重复),就得在去重前先备份键名映射。



















