← PHP array_merge() 函数:合并数组时键名冲突怎么处理 没有下一篇了 →

PHP array_merge_recursive() 函数:递归合并数组,键冲突不再简单覆盖

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

一、先搞清楚它和array_merge()差在哪

PHP内置的array_merge_recursive(),从名字就能看出它和array_merge()是亲戚。它的作用也是把两个或多个数组的元素合并成一个数组,值按顺序追加。这个函数从PHP4.0.1开始就有了,比array_merge()晚一点点。

语法形式:

array array_merge_recursive(array $array1 [, array $...]);

$array1必须传,后面的数组可选,可以传多个。返回值是合并后的数组。

那它和array_merge()到底差在哪?关键就两个字:递归。

上一节讲array_merge()时说过,字符串键冲突时,后面的值直接覆盖前面的。而array_merge_recursive()遇到同样的冲突,不会覆盖,而是把两个值合并成一个数组,都保留下来。这就是它存在的意义。

一句话概括:array_merge()冲突就覆盖,array_merge_recursive()冲突就打包成数组。

二、字符串键冲突:合并成数组

看第一个例子,用代码号学习编程的场景来说明:

<?php
$player1 = array("a" => "sachin", "b" => "virat");
$player2 = array("c" => "yuvraj", "b" => "ganguly");
print_r(array_merge_recursive($player1, $player2));
?>

输出:

Array (
    [a] => sachin
    [b] => Array ( [0] => virat [1] => ganguly )
    [c] => yuvraj
)

b这个键在两个数组里都有。如果是array_merge(),结果会是[b]=>ganguly,virat被丢掉。但array_merge_recursive()把两个值都留下了,b变成了一个包含virat和ganguly的数组。

a和c没有冲突,各自保留原值。

这个行为在什么场景下有用?比如你要统计两份名单里同一个人出现的不同信息,或者合并两组标签时不想丢失任何一项。用array_merge()会丢数据,用array_merge_recursive()能把冲突项都收集起来。

但也要注意,冲突键变成数组后,数据结构就变了。后面如果还按字符串去用这个键,代码可能会出问题。所以用之前要想清楚,这个键变成数组是不是你想要的。

三、键不冲突时:和普通合并没区别

如果两个数组的键没有重叠,array_merge_recursive()的表现和array_merge()一样,就是简单地把值拼在一起。

<?php
$site1 = array("a" => "java", "b" => "T");
$site2 = array("z" => "point", "d" => "tutorial");
print_r(array_merge_recursive($site1, $site2));
?>

输出:

Array ( [a] => java [b] => T [z] => point [d] => tutorial )

四个键a、b、z、d各不相同,没有任何冲突,合并后就是四个键值对平铺在一起。这种情况下,它和array_merge()的结果一致。

所以判断该用哪个函数,取决于你的数据里键有没有可能撞车,以及撞车时你希望怎么处理。键不会冲突,两者随便用;键可能冲突且不想丢数据,用递归版。

四、多个键冲突的情况

当冲突的键不止一个时,每个冲突键都会各自变成数组。

<?php
$color1 = ['A' => 'Black', 'B' => 'Green', 'C' => 'Yellow', 'D' => 'White', 'E' => 'Cyan'];
$color2 = ['F' => 'Aero', 'B' => 'Almond', 'H' => 'Blood', 'D' => 'Coconut', 'J' => 'Dark brown'];
$color = array_merge_recursive($color1, $color2);
print_r($color);
?>

输出:

Array (
    [A] => Black
    [B] => Array ( [0] => Green [1] => Almond )
    [C] => Yellow
    [D] => Array ( [0] => White [1] => Coconut )
    [E] => Cyan
    [F] => Aero
    [H] => Blood
    [J] => Dark brown
)

B和D都冲突了,各自变成了数组。B里装了Green和Almond,D里装了White和Coconut。其他不冲突的键保持原样。

这里能看出一个规律:冲突的键,值按传入数组的顺序追加。先传的在前,后传的在后。顺序是有保证的,不用担心。

再看一个混合场景:

<?php
$input1 = array("a" => "yuvraj", "b" => "sachin", "c" => "rahul");
$input2 = array("d" => "umesh", "a" => "sachin", "e" => "ganguly");
print_r(array_merge_recursive($input1, $input2));
?>

输出:

Array (
    [a] => Array ( [0] => yuvraj [1] => sachin )
    [b] => sachin
    [c] => rahul
    [d] => umesh
    [e] => ganguly
)

a在$input1里是yuvraj,在$input2里是sachin,冲突后合并成[0]=>yuvraj[1]=>sachin。注意这里两个值虽然字面不同,但因为是同一个键,照样合并,不会因为值相同就跳过。

我个人的经验是,这种合并结果在输出时要注意格式。如果直接把[a]打印出来,得到的是Array,不是字符串。前端拿到这种结构可能会懵,所以合并后很好做一层处理,或者提前约定好数据结构。

五、数字键和嵌套数组的行为

前面举的例子都是字符串键。那数字键呢?

array_merge_recursive()对数字键的处理和array_merge()类似:数字键的值按顺序追加,键名重新编号。不同的是,如果数字键对应的值本身是数组,递归合并会继续往里走。

比如:

<?php
$a = array("list" => array(1, 2));
$b = array("list" => array(3, 4));
print_r(array_merge_recursive($a, $b));
?>

输出:

Array (
    [list] => Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
)

list这个键冲突了,但它对应的值是数组,递归合并会把两个数组的内容继续往下合并,而不是把两个数组当成两个元素打包。这是"递归"两个字的真正体现:遇到数组就继续往下走,直到不能再分。

这个特性在处理多层配置、嵌套数据结构时比较实用。但也容易让结果变得比预期复杂,用之前很好先想清楚最终结构长什么样。

六、和array_merge()、+运算符的选型

三个工具摆在一起,怎么选?

array_merge():字符串键冲突时后者覆盖前者,数字键重排。适合"新数据覆盖旧数据"的场景,比如配置合并、参数覆盖。

array_merge_recursive():字符串键冲突时合并成数组,数字键重排,嵌套数组继续递归。适合"冲突数据都要保留"的场景,比如合并多来源的标签、汇总多份名单。

+运算符:键冲突时保留左边的,忽略右边的,数字键不重排。适合"前面的数据优先"的场景。

我的建议是,先想清楚冲突时你要的是什么结果,再选函数。不要因为array_merge_recursive()名字长就觉得它更高级,它只是行为不同。用错了,数据结构会变得难以预期。

还有一点,array_merge_recursive()在PHP8.0之后对类型的要求更严格了,传非数组参数会报TypeError。老代码迁移时要注意这个变化。

七、本节课程知识要点

  • array_merge_recursive()递归合并一个或多个数组,返回新数组。

  • 字符串键冲突时,不覆盖,而是把值合并成数组。

  • 键不冲突时,表现和array_merge()一致。

  • 冲突键的值按传入顺序追加,先传的在前。

  • 数字键按array_merge()的规则重排编号。

  • 嵌套数组会继续递归合并,不是简单打包。

  • 想保留冲突数据用递归版,想覆盖用array_merge(),想保留左边用+。

  • 合并后数据结构可能变化,后续取值要留意键是否已变成数组。

← PHP array_merge() 函数:合并数组时键名冲突怎么处理 没有下一篇了 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号