一、函数定位与核心机制
array_unique是PHP内置的数组处理函数,从PHP4.0.1开始提供。它的作用是把数组中重复的值去掉,返回一个过滤后的新数组。
这里有几个关键点需要先理清:
-
去重针对的是值,不是键名。函数只关心元素的值是否重复,键名不参与重复判断
-
保留首次出现的元素。多个相同值出现时,第一个被保留,后续的被丢弃
-
键名会被保留。结果数组中保留的是被保留元素的原始键名,不是重新索引的连续数字
-
返回新数组。原数组不被修改,结果是一个新的数组变量
第四点很重要。array_unique和uksort不同,它不传引用,不修改原数组,而是返回一个新数组。写代码时可以放心地$result=array_unique($input);,原数组$input保持不变。
语法结构
array array_unique(array $array, int $sort_flags = SORT_STRING): array
参数说明:
| 参数 | 是否必需 | 说明 |
|---|---|---|
| array | 必需 | 输入数组 |
| sort_flags | 可选 | 排序标志,决定值的比较规则,默认SORT_STRING |
sort_flags的四种取值
第二个参数决定去重时用什么规则来判定「两个值是否相等」:
| 标志 | 比较规则 | 典型用途 |
|---|---|---|
| SORT_REGULAR | 按PHP常规规则比较 | 混合类型数组 |
| SORT_NUMERIC | 按数值比较 | 数字数组 |
| SORT_STRING | 按字符串比较(默认) | 字符串数组 |
| SORT_LOCALE_STRING | 按当前区域设置的字符串规则比较 | 多语言场景 |
默认值是SORT_STRING,意味着即使数组里是数字,也会先转成字符串再比较。这一点在数字数组去重时容易踩坑,后面会展开。
二、比较规则的实际差异
用一个例子说明SORT_STRING和SORT_NUMERIC的区别:
$data = array("1", "01", 1, 1.0);
print_r(array_unique($data, SORT_STRING));
用SORT_STRING时,"1"、"01"、1、1.0都转成字符串比较:"1"、"01"、"1"、"1"。结果保留"1"和"01",因为"1"和"01"字符串不相等。
换成SORT_NUMERIC:
print_r(array_unique($data, SORT_NUMERIC));
四个值数值上都等于1,结果只保留第一个"1"。
这个差异在项目中影响很大。从表单接收的数据、从数据库读出的数据,类型可能不统一。如果用默认的SORT_STRING,字符串"1"和整数1会被当作不同值保留下来,去重不彻底。如果业务上认为它们相等,就得显式指定SORT_NUMERIC或SORT_REGULAR。
个人经验:在用户标签去重场景里,标签ID从不同接口返回,有的接口给字符串、有的给整数。一开始用默认参数,结果同一个标签出现两次,一次是"12"一次是12。排查了半天才定位到是sort_flags的问题。后来统一在去重前用array_map做类型转换,或者显式传SORT_REGULAR,问题就解决了。
三、键名保留机制
array_unique保留被保留元素的原始键名,不重新索引。这一点和很多人的直觉不同。
$lang = array('java', 'php', 'java', 'python', 'html');
$result = array_unique($lang);
print_r($result);
输出:
Array
(
[0] => java
[1] => php
[3] => python
[4] => html
)
键名2缺失,因为索引2的java和索引0的java重复,被丢弃了。结果数组的键名是0、1、3、4,不连续。
如果后续逻辑依赖连续的键名(比如用for循环遍历、用json_encode输出成数组格式),需要手动用array_values重新索引:
$result = array_values(array_unique($lang));
这一点在返回JSON给前端时特别重要。如果不去重后重新索引,json_encode会把结果输出成对象而不是数组,前端拿到的数据结构会变。
四、关联数组的去重行为
对关联数组去重时,键名会保留,但重复值的判断只看值。
$news = array("a" => "zee", "b" => "zee", "c" => "aajtak", "d" => "ndtv");
print_r(array_unique($news));
输出:
Array
(
[a] => zee
[c] => aajtak
[d] => ndtv
)
键名a和b的值都是zee,保留先出现的a,b被丢弃。键名c和d的值不重复,全部保留。
这里有一个值得注意的场景:如果两个元素的键名相同但值不同,在PHP数组里这本身就不可能出现(后写的会覆盖先写的)。所以array_unique处理的是值层面的重复。
五、项目实战示例
示例一:字符串数组去重
<?php
$lang = array('java', 'php', 'java', 'python', 'html');
$result = array_unique($lang);
print_r($result);
输出:
Array
(
[0] => java
[1] => php
[3] => python
[4] => html
)
键名2的java和键名0重复,被丢弃,结果键名不连续。
示例二:关联数组去重
<?php
$singer = array("a" => "abhijeet", "b" => "arijit", "c" => "abhijeet");
print_r(array_unique($singer));
输出:
Array
(
[a] => abhijeet
[b] => arijit
)
键名a和c的值都是abhijeet,保留先出现的a,c被丢弃。
示例三:数字字符串与整数的混合去重
<?php
$laptop = array("a" => "dell", 0 => "hp", "b" => "dell", 1 => "lenovo", 2 => "hp");
$result = array_unique($laptop);
print_r($result);
输出:
Array
(
[a] => dell
[0] => hp
[1] => lenovo
)
dell在键名a和b各出现一次,保留a。hp在键名0和2各出现一次,保留0。lenovo不重复,保留。结果键名是a、0、1,混合了字符串和数字。
示例四:去重后重新索引
<?php
$tags = array("php", "mysql", "php", "redis", "mysql");
$result = array_values(array_unique($tags));
print_r($result);
输出:
Array
(
[0] => php
[1] => mysql
[2] => redis
)
用array_values把键名重新索引成0、1、2,方便后续用for循环或输出成JSON数组。
示例五:数字数组用SORT_NUMERIC
<?php
$ids = array("12", 12, "012", 12.0);
print_r(array_unique($ids, SORT_NUMERIC));
输出:
Array
(
[0] => 12
)
四个值数值上都等于12,用SORT_NUMERIC只保留第一个。如果换成默认的SORT_STRING,"12"、"12"、"012"、"12"中"012"和"12"字符串不相等,会保留两个元素。
六、容易踩的坑
默认SORT_STRING导致的类型误判。字符串"1"和整数1在默认规则下转成字符串后相等,但字符串"01"和"1"不相等。如果业务上认为它们相同,需要显式指定sort_flags。
键名不连续影响后续逻辑。去重后键名保留原始索引,可能出现0、1、3、4这样的跳号。用for循环遍历会漏元素,用json_encode会输出成对象。需要连续索引时用array_values包一层。
SORT_REGULAR的松散比较。SORT_REGULAR用的是PHP的松散比较规则,在PHP8之前,"1abc"==1会判为true,PHP8之后行为有变化。如果数组里有这类混合值,去重结果和预期可能不同。建议在去重前先规范数据类型。
多维数组不适用。array_unique只能处理一维数组,遇到子数组会报「Arraytostringcorsion」的提示,或者用SORT_REGULAR时按数组引用比较,结果不可预期。多维数组去重需要自己写递归或序列化后去重。
大数据量下的性能。array_unique内部会做排序,时间复杂度大约是O(nlogn)。数据量很大时如果只是想去重,用array_flip配合array_keys在某些场景下更快,因为哈希表查找是O(n)。不过array_flip只能处理字符串和整数,且会丢失键名信息。
七、本节课程知识要点
-
array_unique针对值去重,键名不参与重复判断
-
重复时保留首次出现的元素,丢弃后续出现的
-
结果数组保留原始键名,不重新索引
-
返回新数组,不修改原数组
-
sort_flags决定比较规则,默认是SORT_STRING
-
字符串"1"和整数1在默认规则下相等,字符串"01"和"1"不等
-
需要连续索引时用array_values包一层
-
多维数组去重需要额外处理
八、适用场景
这个函数适合以下场景:
-
一维字符串数组去重
-
关联数组按值去重,保留原始键名
-
表单多选值去重
-
标签、分类等简单列表去重
不适合的场景:
-
多维数组去重,需要自己写递归逻辑
-
需要保留所有重复项的场景
-
数据量特别大且只需去重,可考虑array_flip方案
-
需要按键名去重的场景,用array_unique不适用,得用其他方法