array_merge($a, $b) 返回一个新数组,先是 $a 的元素,后面接着 $b 的元素。数字键会从 0 开始重新编号;当两个数组有相同的字符串键时,以后面数组中的值为准。你可以传入任意多个数组。
array_merge() 不会修改它的参数。它返回合并后的数组,所以要把结果赋值出去:$all = array_merge($all, $more);。
数字键会被重新编号
array_merge() 把整数键当作位置,而不是名字。每个整数键的元素都会被追加,并从 0 开始获得新的键,即使原数组用的是相同的数字或者键不连续。对于列表来说这正是你想要的,但当这些数字是 ID 时就会让你意外。
看起来像整数的字符串键('101')会被存储为整数,所以它们也会被重新编号。像 'id101' 这样的键则保持不变。
array_merge 与 + 运算符
+ 运算符(数组联合)也能合并数组,但规则正好相反:对于左边数组中已经存在的键,以左边的值为准,右边的值被丢弃。它从不重新编号。因此 + 最适合用来补全默认值,而 array_merge() 最适合用来覆盖默认值。
array_merge($a, $b) | $a + $b | |
|---|---|---|
| 相同的字符串键 | 取 $b 中的值 | 取 $a 中的值 |
| 整数键 | 重新编号,保留所有值 | 保留键,冲突时丢弃 $b 的值 |
| 典型用途 | 连接列表、覆盖设置 | 补全默认值、保留 ID |
代码块中的最后一个例子是经典的 + 陷阱:['a', 'b'] + ['x', 'y', 'z'] 得到的是 a, b, z,因为键 0 和 1 在左边已经存在。
展开语法:[...b]
展开运算符在数组字面量中把数组展开。它的行为和 array_merge() 一样:整数键会重新编号;从 PHP 8.1 开始也允许字符串键,并以后面的为准。当你要把数组和单个值混在一起时,这种写法读起来很清楚。
如果要把一个数组直接追加到已有的数组上,请看 array_push() 配合展开运算符。
合并一个由数组组成的数组
当要合并的数组本身就放在一个列表里时,把这个列表展开传给 array_merge()。这也是在循环中合并的高效方式:先收集各个部分,再一次性合并。
在循环里写 $all = array_merge($all, $batch); 也能工作,但它每一轮都会复制整个不断变大的数组,元素有几千个时就会变慢。
array_merge_recursive 和 array_replace
array_merge_recursive() 也会合并嵌套数组,但对于保存普通值的重复字符串键,它不会覆盖,而是把两个值收集到一个新数组里。对于配置来说,这很少是你想要的。这种情况下通常该用 array_replace_recursive():它会在每一层覆盖值,并保留键。
从上到下看这三行 JSON:array_merge() 替换了整个 db 数组,丢失了 host;array_merge_recursive() 把 port 和 debug 变成了包含两个元素的列表;而 array_replace_recursive() 生成了你真正想要的配置。
常见错误:合并不会去掉任何东西
array_merge() 会保留位于不同键下的重复值。合并两个标签列表时,两者共有的每个标签都会出现两次。把结果先交给 array_unique(),再用 array_values() 重新编号。
array_unique 页面介绍了如何从多行数据和嵌套数组中去除重复项。
常见问题
PHP 中 array_merge 和 + 有什么区别?
对于重复的字符串键,array_merge($a, $b) 保留 $b 中的值,而 $a + $b 保留 $a 中的值。对于数字键,array_merge() 会把所有元素追加并重新编号,而 + 会保留数字键,并丢弃 $b 中键已经存在于 $a 的元素。
怎样合并数组并保留数字键?
如果应该以左边的数组为准,用 $a + $b;如果应该以右边的数组为准,用 array_replace($a, $b)。array_merge() 总是把数字键从 0 开始重新编号。
PHP 中怎样合并一个由数组组成的数组?
把外层数组展开传给 array_merge():$all = array_merge(...$lists);。列表为空时它返回 []。先在循环中收集各个数组,最后一次性合并;在循环里调用 array_merge() 每次都会复制不断变大的结果。
PHP 的展开运算符可以用于字符串键吗?
可以,从 PHP 8.1 开始。这时 [...$a, ...$b] 的行为和 array_merge($a, $b) 一样:数字键会重新编号,后面的字符串键会覆盖前面的。