PHP 数组合并与组合:深度解析不同场景下的数组相加方法141
在 PHP 编程中,数组是一种极其灵活且功能强大的数据结构,广泛应用于存储和管理各种类型的数据。随着应用程序的复杂性增加,我们经常需要将两个或多个数组合并、组合或“相加”起来,以构建新的数据集合。然而,“数组相加”并非一个简单的算术操作,PHP 提供了多种方法来实现这一目标,每种方法都有其独特的行为和适用场景。理解这些方法的差异,对于编写高效、健壮的 PHP 代码至关重要。
本文将作为一份详尽的指南,深度解析 PHP 中各种“数组相加”的方法,包括它们如何处理索引数组和关联数组,以及在不同业务场景下应如何选择最合适的工具。我们将从基础运算符到高级函数,逐一剖析,并通过丰富的代码示例,帮助您彻底掌握 PHP 数组的合并与组合技巧。
理解 PHP 中“数组相加”的含义
在 PHP 中,当我们谈论“数组相加”时,它通常不是指像数字那样进行简单的算术求和。相反,它更多地指的是以下几种操作:
合并 (Merging): 将一个数组的元素添加到另一个数组中,可能会覆盖或保留现有元素。
组合 (Combining): 从两个数组中分别取键和值来创建一个新的关联数组。
替换 (Replacing): 用一个数组的元素替换另一个数组中具有相同键的元素。
追加 (Appending): 将一个数组的元素简单地追加到另一个数组的末尾。
由于这些操作的粒度不同,PHP 提供了专门的运算符和函数来满足不同的需求。接下来,我们将逐一探索。
一、使用 `+` 运算符进行数组联合 (Array Union)
PHP 的 `+` 运算符在应用于数组时,执行的是数组的“联合”操作,而不是算术加法。它的核心规则是:将右侧数组中所有在左侧数组中不存在的键/值对添加到左侧数组中。如果键在左侧数组中已经存在,则左侧数组的值将保持不变,右侧数组中相同键的值会被忽略。
1.1 索引数组的 `+` 运算符
对于纯数字索引的数组,`+` 运算符的行为可能有些出乎意料。它会保留左侧数组的现有索引,并将右侧数组中那些在左侧数组中不存在的索引及对应值添加进来。这意味着,如果两个数组都从 0 开始索引,并且左侧数组有 0, 1, 2,右侧数组也有 0, 1, 2,那么右侧的 0, 1, 2 将被忽略。<?php
$array1 = [1, 2, 3]; // 隐式键: 0, 1, 2
$array2 = [4, 5, 6]; // 隐式键: 0, 1, 2
$result = $array1 + $array2;
print_r($result);
/*
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
)
*/
$array3 = [0 => 'a', 2 => 'b'];
$array4 = [1 => 'x', 3 => 'y'];
$result2 = $array3 + $array4;
print_r($result2);
/*
Output:
Array
(
[0] => a
[2] => b
[1] => x
[3] => y
)
*/
?>
在第一个例子中,`$array1` 的键 0, 1, 2 已经存在,所以 `$array2` 的相同键被忽略。在第二个例子中,由于 `$array3` 没有键 1 和 3,所以 `$array4` 的这些键值对被添加进来。
1.2 关联数组的 `+` 运算符
对于关联数组,`+` 运算符的行为更加直观。它会优先保留左侧数组的键值对。如果右侧数组中存在与左侧数组相同的键,则右侧的值会被忽略。只有右侧数组中那些在左侧数组中不存在的键值对才会被添加。<?php
$configDefault = [
'host' => 'localhost',
'port' => 3306,
'user' => 'root'
];
$configUser = [
'port' => 8888, // 此键在 $configDefault 中已存在,将被忽略
'password' => '123456' // 新键,将被添加
];
$finalConfig = $configDefault + $configUser;
print_r($finalConfig);
/*
Output:
Array
(
[host] => localhost
[port] => 3306
[user] => root
[password] => 123456
)
*/
?>
适用场景: `+` 运算符非常适合用于设置默认配置,其中 `$configDefault` 包含所有默认值,而 `$configUser` 只包含用户自定义的覆盖值。这种方式可以确保默认值在未被显式覆盖时得到保留。
二、使用 `array_merge()` 函数进行数组合并
`array_merge()` 是 PHP 中最常用的数组合并函数,它能将一个或多个数组合并为一个。它的核心特点是:对于字符串键,后面的值会覆盖前面的值;对于数字键,值会被重新索引并追加。
2.1 索引数组的 `array_merge()`
当合并索引数组时,`array_merge()` 会将所有数字键视为普通元素,并将它们追加到结果数组的末尾,然后对整个结果数组重新进行数字索引。这意味着原有的数字键会被丢弃,并从 0 开始重新分配。<?php
$array1 = [1, 2, 3];
$array2 = [4, 5, 6];
$result = array_merge($array1, $array2);
print_r($result);
/*
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
)
*/
$array3 = [0 => 'a', 2 => 'b'];
$array4 = [1 => 'x', 3 => 'y'];
$result2 = array_merge($array3, $array4);
print_r($result2);
/*
Output:
Array
(
[0] => a
[1] => b
[2] => x
[3] => y
)
*/
?>
注意 `$result2` 中,原来的键 0, 2, 1, 3 都被重新索引为 0, 1, 2, 3。
2.2 关联数组的 `array_merge()`
当合并关联数组时,`array_merge()` 的行为是:如果多个输入数组中存在相同的字符串键,则后一个数组的值会覆盖前一个数组中对应键的值。如果键是唯一的,则直接添加到结果数组中。<?php
$settingsDefault = [
'theme' => 'light',
'language' => 'en',
'debug' => false
];
$settingsUser = [
'language' => 'zh', // 覆盖 'en'
'notifications' => true // 新增
];
$finalSettings = array_merge($settingsDefault, $settingsUser);
print_r($finalSettings);
/*
Output:
Array
(
[theme] => light
[language] => zh
[debug] =>
[notifications] => 1
)
*/
?>
适用场景: `array_merge()` 是最通用的合并函数,尤其适用于当您希望后续数组的相同键值覆盖前面数组的值,并且对于数字键,希望它们能被简单地追加并重新索引的场景。例如,合并数据库查询结果、收集用户输入数据等。
2.3 合并多个数组
`array_merge()` 可以接受任意数量的数组作为参数:<?php
$arr1 = ['a' => 1, 'b' => 2];
$arr2 = ['b' => 3, 'c' => 4];
$arr3 = ['c' => 5, 'd' => 6];
$mergedArray = array_merge($arr1, $arr2, $arr3);
print_r($mergedArray);
/*
Output:
Array
(
[a] => 1
[b] => 3
[c] => 5
[d] => 6
)
*/
?>
从输出可以看出,键 'b' 的最终值是 `3` (来自 `$arr2`),键 'c' 的最终值是 `5` (来自 `$arr3`)。
三、使用 `array_replace()` 函数进行数组替换
`array_replace()` 函数的行为与 `array_merge()` 类似,但它在处理数字键时有所不同:`array_replace()` 会保留原有的数字键,并用替换数组中的对应键值覆盖它们,而不是重新索引。对于在原始数组中不存在的键,无论是数字还是字符串,都会被添加到结果数组中。
3.1 索引数组的 `array_replace()`
`array_replace()` 在处理数字键时,会用后续数组的同名数字键的值来覆盖前面数组的值。如果后续数组有新的数字键,它们会被添加进来,而不是重新索引。<?php
$array1 = ['apple', 'banana', 'cherry']; // 0, 1, 2
$array2 = [1 => 'grape', 3 => 'date'];
$result = array_replace($array1, $array2);
print_r($result);
/*
Output:
Array
(
[0] => apple
[1] => grape // 'banana' 被 'grape' 替换
[2] => cherry
[3] => date // 新增
)
*/
$array3 = [0 => 'a', 2 => 'b'];
$array4 = [1 => 'x', 3 => 'y'];
$result2 = array_replace($array3, $array4);
print_r($result2);
/*
Output:
Array
(
[0] => a
[2] => b
[1] => x
[3] => y
)
*/
?>
在 `$result2` 的例子中,`array_replace()` 的行为与 `+` 运算符相同,因为 `$array3` 中没有键 1 和 3,所以 `$array4` 的键值对被添加进来。
3.2 关联数组的 `array_replace()`
对于关联数组,`array_replace()` 的行为与 `array_merge()` 完全相同:后一个数组的相同字符串键会覆盖前一个数组的值。<?php
$dataOriginal = [
'id' => 101,
'name' => 'Alice',
'status' => 'active'
];
$dataUpdate = [
'name' => 'Alicia', // 覆盖 'Alice'
'email' => 'alicia@' // 新增
];
$updatedData = array_replace($dataOriginal, $dataUpdate);
print_r($updatedData);
/*
Output:
Array
(
[id] => 101
[name] => Alicia
[status] => active
[email] => alicia@
)
*/
?>
适用场景: `array_replace()` 特别适合用于需要更新现有数组中特定位置(通过数字键或字符串键)的元素,同时保留其他元素及其原始索引的场景。例如,更新一个由数据库查询返回的行数组。
四、使用 `array_merge_recursive()` 进行递归合并
当处理包含多层嵌套数组的数据结构时,`array_merge()` 和 `array_replace()` 都会直接覆盖子数组,而不是合并它们。此时,`array_merge_recursive()` 就派上用场了。
`array_merge_recursive()` 会递归地合并数组。如果两个数组有相同的字符串键,并且对应的值都是数组,它会继续递归合并这些子数组。如果值不是数组(或只有一个是数组),它会将所有同键的值放入一个新的数组中。<?php
$config1 = [
'database' => [
'host' => 'localhost',
'port' => 3306
],
'logging' => true,
'users' => ['admin']
];
$config2 = [
'database' => [
'port' => 8888, // 覆盖
'user' => 'root' // 新增
],
'logging' => false, // 值不是数组,会被放入一个新数组
'users' => ['guest'] // 值不是数组,会被放入一个新数组
];
$mergedConfig = array_merge_recursive($config1, $config2);
print_r($mergedConfig);
/*
Output:
Array
(
[database] => Array
(
[host] => localhost
[port] => 8888
[user] => root
)
[logging] => Array // 注意这里,由于值不是数组,它被合并成了一个数组
(
[0] => 1
[1] =>
)
[users] => Array // 同上
(
[0] => admin
[1] => guest
)
)
*/
?>
注意事项: `array_merge_recursive()` 在合并非数组的同键值时,会将它们合并成一个新的索引数组。这可能不是您总是希望的行为,特别是在处理配置覆盖时。在这种情况下,您可能需要实现一个自定义的递归合并函数,或者使用一些框架提供的深度合并工具(例如 Laravel 的 `Arr::except()` 和 `Arr::undot()` 结合实现,或 Symfony 的 `array_replace_recursive()` 行为更符合预期)。<?php
// 自定义一个更符合配置覆盖逻辑的深度合并函数示例
function array_merge_recursive_distinct(array &$array1, array &$array2): array
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
$config1 = [
'database' => [
'host' => 'localhost',
'port' => 3306
],
'logging' => true,
'users' => ['admin']
];
$config2 = [
'database' => [
'port' => 8888,
'user' => 'root'
],
'logging' => false,
'users' => ['guest']
];
$mergedConfigDistinct = array_merge_recursive_distinct($config1, $config2);
print_r($mergedConfigDistinct);
/*
Output:
Array
(
[database] => Array
(
[host] => localhost
[port] => 8888
[user] => root
)
[logging] =>
[users] => Array
(
[0] => guest
)
)
*/
?>
适用场景: 深度合并多层嵌套的配置文件、默认选项和用户自定义选项。
五、使用 `array_combine()` 函数创建关联数组
`array_combine()` 函数的目的是从两个独立的数组中创建一个新的关联数组:一个数组的值作为新数组的键,另一个数组的值作为新数组的值。<?php
$keys = ['id', 'name', 'email'];
$values = [1, 'John Doe', 'john@'];
$user = array_combine($keys, $values);
print_r($user);
/*
Output:
Array
(
[id] => 1
[name] => John Doe
[email] => john@
)
*/
?>
限制: `array_combine()` 要求两个输入数组的元素数量必须严格相等,否则会返回 `false`。
适用场景: 当您有分开的键列表和值列表,并希望将它们组合成一个关联数组时,例如从两列数据库查询结果中构建数据结构。
六、使用 `array_map()` 和 `array_sum()` 进行元素级数值相加
如果“数组相加”的含义是两个相同大小的数组,其对应位置的数字元素进行算术求和,那么我们可以结合使用 `array_map()` 和 `array_sum()` (或者一个循环) 来实现。<?php
$quantities1 = [10, 20, 30];
$quantities2 = [5, 15, 25];
// 方式一:使用 array_map 进行元素级相加
$summedQuantities = array_map(function($q1, $q2) {
return $q1 + $q2;
}, $quantities1, $quantities2);
print_r($summedQuantities);
/*
Output:
Array
(
[0] => 15
[1] => 35
[2] => 55
)
*/
// 如果是关联数组,需要确保键的对应
$salesMonday = ['apples' => 10, 'bananas' => 5];
$salesTuesday = ['apples' => 8, 'bananas' => 12];
$totalSales = [];
foreach ($salesMonday as $item => $count) {
$totalSales[$item] = $count + ($salesTuesday[$item] ?? 0); // 使用 ?? 处理可能不存在的键
}
foreach ($salesTuesday as $item => $count) {
if (!isset($totalSales[$item])) {
$totalSales[$item] = $count;
}
}
print_r($totalSales);
/*
Output:
Array
(
[apples] => 18
[bananas] => 17
)
*/
?>
适用场景: 统计数据累加、合并数值型报表数据等。
七、简单的追加操作:`array_push()` 和 `[]` 语法
虽然这并非严格意义上的“数组相加”,但将一个或多个元素追加到现有数组的末尾是数组操作中非常常见的一部分。<?php
$list = ['item1', 'item2'];
// 使用 array_push() 添加一个或多个元素
array_push($list, 'item3', 'item4');
print_r($list);
/*
Output:
Array
(
[0] => item1
[1] => item2
[2] => item3
[3] => item4
)
*/
// 使用 [] 语法添加单个元素(PHP 5.4+)
$list[] = 'item5';
print_r($list);
/*
Output:
Array
(
[0] => item1
[1] => item2
[2] => item3
[3] => item4
[4] => item5
)
*/
// 合并另一个数组到现有数组末尾(array_merge 的简化版,如果只关心数字索引的追加)
$list = array_merge($list, ['item6', 'item7']);
print_r($list);
?>
适用场景: 动态构建列表、收集用户输入等。
八、性能考量与最佳实践
对于大多数Web应用而言,数组合并函数的性能差异在处理中小规模数组时通常可以忽略不计。然而,当处理包含数万甚至数十万元素的超大数组时,性能差异可能会变得显著。
`array_merge()` 通常被认为是效率较高的合并函数,因为它在底层优化了内存分配。
`+` 运算符在某些场景下可能会比 `array_merge()` 稍快,因为它避免了重新索引数字键的开销。
`array_merge_recursive()` 由于其递归特性,在处理深层嵌套的超大数组时,性能开销会更大。
最佳实践:
明确目的: 在选择函数之前,首先清楚你想要如何“相加”数组:是覆盖、追加、合并唯一键、还是进行数学运算?
理解键处理: 特别注意所选函数如何处理数字键和字符串键,以及它们是否会覆盖或保留值。
避免不必要的复杂性: 如果 `array_merge()` 就能满足需求,就不要使用更复杂的 `array_merge_recursive()`。
注意 `array_merge_recursive()` 的副作用: 记住它会为非数组的同键值创建新的子数组,这可能不是预期行为。考虑自定义递归函数或使用更专业的库函数。
测试与验证: 在实际应用中,尤其是在生产环境中,务必对合并结果进行 `var_dump()` 或 `print_r()` 检查,以确保它们符合预期。
大型数组的优化: 如果您的应用需要频繁处理非常大的数组,并且对性能有严格要求,可以考虑进行基准测试,或者探讨更底层的数据结构或算法优化。
PHP 提供了强大且多样的工具来处理数组的合并与组合。从简单的 `+` 运算符到功能丰富的 `array_merge()`、`array_replace()` 和 `array_merge_recursive()`,每种方法都服务于特定的场景。选择正确的“数组相加”方法是编写高效、可维护代码的关键。
使用 `+` 运算符 实现数组联合,当您希望保留左侧数组的现有键,并只添加右侧数组中不存在的新键时。
使用 `array_merge()` 进行通用合并,当您希望数字键被重新索引,而字符串键由后续数组覆盖时。
使用 `array_replace()` 进行替换式合并,当您希望保留数字键并用后续数组的对应键值覆盖,或添加新键时。
使用 `array_merge_recursive()` 进行深度合并,处理嵌套数组,但需注意其对非数组值的特殊处理。
使用 `array_combine()` 从两个数组中分别提取键和值来创建新的关联数组。
对于元素级的数值求和,可以结合 `array_map()` 和循环来实现。
通过深入理解这些方法的行为差异和适用场景,您将能够更加自信和高效地处理 PHP 中的数组操作,从而构建出更加健壮和灵活的应用程序。
2025-09-30

Java编程入门:初学者掌握核心方法与学习重点的全面指南
https://www.shuihudhg.cn/127991.html

Python 读取数据列:从入门到精通,高效提取与处理指南
https://www.shuihudhg.cn/127990.html

PHP 获取 APK 应用名称:实用方法与代码解析
https://www.shuihudhg.cn/127989.html

Python封装的艺术:深入理解私有与保护函数(`_`与`__`的哲学)
https://www.shuihudhg.cn/127988.html

Java时间戳全面指南:从基础概念到JSR-310现代API最佳实践
https://www.shuihudhg.cn/127987.html
热门文章

在 PHP 中有效获取关键词
https://www.shuihudhg.cn/19217.html

PHP 对象转换成数组的全面指南
https://www.shuihudhg.cn/75.html

PHP如何获取图片后缀
https://www.shuihudhg.cn/3070.html

将 PHP 字符串转换为整数
https://www.shuihudhg.cn/2852.html

PHP 连接数据库字符串:轻松建立数据库连接
https://www.shuihudhg.cn/1267.html