← PHP array_uintersect_assoc函数详解:带键名校验的自定义交集运算 PHP array_uintersect_uassoc函数详解:键与值双重自定义回调的交集运算 →

PHP array_uintersect函数详解:自定义回调驱动的多数组值交集运算

著
原创 2026-10-09 PHP 已有人查阅

一、函数定位与核心机制

array_uintersect是PHP内置的数组处理函数,从PHP5.0开始提供。它的名字可以拆成两部分理解:

  • uintersect:u代表userfunction,即使用用户自定义的回调函数来比较数组元素的值

  • intersect:计算交集,返回第一个数组中那些「值存在于后续所有数组中」的元素

与array_intersect的区别在于,array_intersect用PHP内部的字符串比较规则来比对值,而array_uintersect把值的比较权交给开发者。这意味着开发者可以自定义大小写规则、数值精度、对象属性比对等逻辑。

语法结构

array_uintersect(
    array $array1,
    array $array2,
    array ...$arrays,
    callable $value_compare_func
): array

参数说明:

参数 是否必需 说明
array1 必需 被比较的主数组,结果从这个数组里取
array2 必需 参与比较的数组
...$arrays 可选 更多参与比较的数组
value_compare_func 必需 用户自定义的值比较函数

回调函数在参数列表的之后,这个位置安排和array_uintersect_assoc一致。数组数量不固定时,回调必须放在所有数组之后。

比较逻辑拆解

array_uintersect的执行分三步:

  1. PHP内部先对参与比较的数组进行排序,排序时调用用户提供的回调函数

  2. 排序完成后,逐个比对元素值,回调返回0表示相等

  3. 把array1中值匹配的元素挑出来,保留其原始键名,组成结果数组返回

关键点在于:只比较值,不比较键名。这是它与array_uintersect_assoc的本质区别。键名在结果中会被保留,但键名本身不参与匹配判断。

二、与相近函数的区分

这几个函数在项目中经常被混用,先理清楚:

函数 键名是否参与比较 值比较方式
array_intersect 否 字符串比较
array_intersect_assoc 是 字符串比较
array_uintersect 否 用户自定义回调
array_uintersect_assoc 是 用户自定义回调

个人经验:在权限系统中比对用户角色列表时,两个数组的键名可能一个是数字索引、一个是角色ID字符串,但值都是角色名称。这种情况下用array_uintersect是合理的,因为只关心角色名是否重叠。但如果角色名相同却属于不同资源域(键名代表资源域),就必须换用array_uintersect_assoc,否则会把不同域的同名角色误判为交集。

三、回调函数的编写规范

回调函数接收两个参数,返回整数:

function codehub_compare($a, $b) {
    if ($a === $b) {
        return 0;
    }
    return ($a > $b) ? 1 : -1;
}

几个实操要点:

返回值必须规范。PHP只关心返回值是否为0,但返回1和-1有助于内部排序的稳定。如果随便返回true/false,在某些版本下可能触发意外排序,结果不可预期。

优先用全等===判断。松散比较==会把"0"和0判为相等,在金额、编号等场景下容易出问题。数据库读出来的字符串"0"和整型0业务含义可能不同。

回调必须对称。内部排序后,a和a和b哪个来自array1、哪个来自array2是随机的。回调不能依赖参数来源做逻辑分支,否则结果会随数组长度变化而漂移。

回调里不要做副作用操作。这个函数内部会对数组排序,回调的调用次数和顺序并不固定。在回调里写日志、改全局变量,结果会不可预期。这一点我在早期项目中踩过坑:在回调里累加计数器想统计比较次数,结果发现同一个元素被比较了多次,计数对不上。

四、项目实战示例

示例一:基础值交集

<?php
function codehub_cmp($a, $b) {
    if ($a === $b) {
        return 0;
    }
    return ($a > $b) ? 1 : -1;
}

$test1 = array("a" => "manual", "b" => "selenium", "d" => "jmeter");
$test2 = array("d" => "automation", "e" => "jmeter", "f" => "beta");

$result = array_uintersect($test1, $test2, "codehub_cmp");
print_r($result);

输出:

Array
(
    [d] => jmeter
)

两个数组中只有jmeter这个值相同,键名d和e不同,但键名不参与比较,所以结果保留array1中的原始键名d。

示例二:键名类型不同的场景

<?php
function codehub_cmp2($v1, $v2) {
    if ($v1 === $v2) {
        return 0;
    }
    return ($v1 > $v2) ? 1 : -1;
}

$lang1 = array("a" => "java", "b" => "c", "c" => "php");
$lang2 = array(1 => "java", 2 => "c", 3 => "csharp");

print_r(array_uintersect($lang1, $lang2, "codehub_cmp2"));

输出:

Array
(
    [a] => java
    [b] => c
)

array1用的是字符串键名,array2用的是数字键名,但值java和c匹配,所以两个元素都被保留。如果换成array_uintersect_assoc,这里的结果会是空数组,因为键名对不上。

示例三:大小写不敏感的比对

<?php
$lang1 = array("a" => "java", "b" => "php", "c" => "perl", "python");
$lang2 = array("a" => "JAVA", "B" => "php", "c", "python");

print_r(array_uintersect($lang1, $lang2, "strcasecmp"));

输出:

Array
(
    [a] => java
    [b] => php
    [0] => python
)

strcasecmp是PHP内置的大小写不敏感字符串比较函数,直接作为回调传入。这里java和JAVA被判为相等,php和php相等,python和python相等。注意结果中保留了array1的原始键名,包括数字索引0。

示例四:多数组交叉比对

<?php
function codehub_score_cmp($v1, $v2) {
    if ($v1 === $v2) {
        return 0;
    }
    return ($v1 > $v2) ? 1 : -1;
}

$score1 = array(40, 53, 43, 23);
$score2 = array(53, 40, 42, 43);
$score3 = array(34, 53, 43, 32);

$result = array_uintersect($score1, $score2, $score3, "codehub_score_cmp");
print_r($result);

输出:

Array
(
    [1] => 53
    [2] => 43
)

三个数组同存在的值是53和43,40只在前两个数组出现,不在第三个数组里,所以被排除。结果保留array1中的原始键名1和2。

五、容易踩的坑

回调参数顺序不确定。内部排序后,a和a和b哪个来自array1、哪个来自array2是随机的。回调必须是对称的,不能依赖参数来源做逻辑分支。

数字索引键名的保留。结果数组中会保留array1的原始键名,包括数字索引。如果后续要用array_values重新索引,记得手动处理。

空数组参与比较。只要有一个数组为空,结果一定是空数组。这个行为符合交集语义,但写代码时如果忘了判空,可能得到意料之外的结果。

性能考量。这个函数内部会对数组排序,时间复杂度大约是O(nlogn)。在数据量超过几千条时,如果回调函数本身很重,整体耗时会明显上升。个人建议:数据量大的场景,先用array_flip或哈希表做初步筛选,再用这个函数做精确比对。如果只是简单的字符串值比对,直接用array_intersect性能更好,没必要上自定义回调。

回调里做类型转换要谨慎。如果数组里混有字符串和数字,回调里的===会让"53"和53判为不相等。如果业务上需要把它们视为相同,得在回调里统一类型,比如都用(string)转换后再比。

六、知识要点归总

  • array_uintersect只比对值,不比对键名,结果保留array1的原始键名

  • 回调函数须返回0表示相等,正负值表示大小关系

  • 回调必须对称,不能依赖参数顺序

  • 与array_uintersect_assoc的区别在于是否校验键名

  • 与array_intersect的区别在于值比较是否走自定义回调

  • 大数据量下注意排序带来的性能开销,必要时先做预处理

七、适用场景

这个函数适合以下场景:

  • 多来源数据的值去重(只关心值是否重叠,不关心键名)

  • 大小写不敏感的字符串比对(用strcasecmp做回调)

  • 浮点数精度范围内的比对(回调里做四舍五入后再比)

  • 对象属性比对(回调里提取对象属性再比)

不适合的场景:

  • 需要同时校验键名的场景,用array_uintersect_assoc

  • 值比对规则简单且固定,用array_intersect性能更好

  • 需要保留重复元素的场景,这个函数不处理重复值

← PHP array_uintersect_assoc函数详解:带键名校验的自定义交集运算 PHP array_uintersect_uassoc函数详解:键与值双重自定义回调的交集运算 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号