一、函数定位与核心机制
array_unshift是PHP内置的数组处理函数,从PHP4.0开始提供。它的作用是把一个或多个元素插入到数组的开头,原数组的其他元素依次后移。
名字可以拆开理解:
-
array:操作对象是数组
-
unshift:与shift相对,shift是从头部弹出元素,unshift是往头部压入元素
这个函数在队列(queue)和栈(stack)结构里用得比较多。队列是先进先出,通常从尾部追加、从头部取出;如果业务上需要反向操作,比如把高优先级任务插到队首,就会用到array_unshift。
语法结构
int array_unshift(array &$array, mixed ...$values): int
参数说明:
| 参数 | 是否必需 | 说明 |
|---|---|---|
| array | 必需 | 输入数组,传引用,函数直接修改原数组 |
| ...$values | 必需 | 要插入到开头的值,可以是一个或多个 |
返回值是插入后数组的新元素个数,类型是整数。注意返回的是数量,不是数组本身。这一点和array_unique返回新数组不同,array_unshift是就地修改原数组。
传引用机制
第一个参数是&$array,传引用。函数执行后原数组被直接改动,新元素出现在开头,原元素依次后移。写代码时不需要重新赋值:
$data = array("b", "c");
array_unshift($data, "a");
// $data 现在是 array("a", "b", "c")
如果业务上需要保留原数组,得先复制一份:
$original = array("b", "c");
$modified = $original;
array_unshift($modified, "a");
这个坑在多层函数调用里容易忽略,导致上游数据被意外改动。
二、键名处理规则
原文里有一条重要说明:所有数值键名会被重新编号,从0开始;字符串键名保持不变。
这是array_unshift里最容易出问题的地方,分开说。
数值键名的重建
$arr = array(0 => "a", 1 => "b", 2 => "c");
array_unshift($arr, "x");
print_r($arr);
输出:
Array
(
[0] => x
[1] => a
[2] => b
[3] => c
)
原来的键名0、1、2全部后移变成1、2、3,新元素占用0。数值键名被重建。
即使原数组的数值键名不是连续的,也会被重建成连续:
$arr = array(5 => "a", 10 => "b");
array_unshift($arr, "x");
print_r($arr);
输出:
Array
(
[0] => x
[1] => a
[2] => b
)
原来的5和10被丢弃,重新编号成1和2。
字符串键名的保留
$arr = array("name" => "Tom", "age" => 20);
array_unshift($arr, "extra");
print_r($arr);
输出:
Array
(
[0] => extra
[name] => Tom
[age] => 20
)
字符串键名name和age保持不变,新插入的元素用数字键名0。
混合键名的场景
$arr = array("name" => "Tom", 0 => "a", 1 => "b");
array_unshift($arr, "x");
print_r($arr);
输出:
Array
(
[0] => x
[name] => Tom
[1] => a
[2] => b
)
字符串键名name保留,数值键名0和1被重建成1和2。新元素占用0。
个人经验:在购物车合并场景里,商品用商品ID作为键名(字符串形式),数量作为值。合并两个购物车时如果用array_unshift把新商品插到开头,商品ID键名会保留,但购物车里如果有用数字索引的临时项,这些索引会被重建,导致后续按索引访问的逻辑出错。后来改成用array_merge或者手动处理键名,避开了这个问题。
三、多元素插入的顺序
array_unshift支持一次插入多个元素,插入顺序按参数从左到右排列。
$arr = array("c");
array_unshift($arr, "a", "b");
print_r($arr);
输出:
Array
(
[0] => a
[1] => b
[2] => c
)
参数a在b前面,插入后a排在b前面。也就是按参数顺序依次放在数组开头,最先传的参数排在结果的最前面。
这一点和很多人直觉一致,但和array_push的行为要区分开。array_push是把元素追加到尾部,也是按参数顺序,最先传的排在最前面(在尾部区域内)。两者在各自区域内都保持参数顺序。
四、项目实战示例
示例一:基础头部插入
<?php
$site = array('java', 't', 'point');
array_unshift($site, 1, 2, 3);
foreach ($site as $element) {
print_r("$element ");
}
输出:
1 2 3 java t point
三个数字1、2、3依次插到开头,原数组元素后移。
示例二:变量作为插入值
<?php
$cricket = array("virat", "rohit", "pandeya");
$a1 = "sachin";
$a2 = "sehwag";
$a3 = "harbhajan";
array_unshift($cricket, $a1, $a2, $a3);
print_r($cricket);
输出:
Array
(
[0] => sachin
[1] => sehwag
[2] => harbhajan
[3] => virat
[4] => rohit
[5] => pandeya
)
插入的值可以是变量,函数按参数顺序依次插入。返回值是6,表示插入后数组有6个元素。
示例三:两个元素插入
<?php
$laptop = array('dell', 'hp');
array_unshift($laptop, 'lenovo', 'acer');
print_r($laptop);
输出:
Array
(
[0] => lenovo
[1] => acer
[2] => dell
[3] => hp
)
lenovo和acer依次插到开头,原数组的dell和hp后移。键名从0开始重新编号。
示例四:字符串键名保留
<?php
$config = array("host" => "localhost", "port" => 3306);
array_unshift($config, "debug");
print_r($config);
输出:
Array
(
[0] => debug
[host] => localhost
[port] => 3306
)
新元素用数字键名0,原有的字符串键名host和port保持不变。这是array_unshift和array_merge在处理字符串键名时的一个区别:array_merge会把字符串键名的值覆盖或追加,而array_unshift只是把新元素插到开头,不动原有的字符串键名。
示例五:获取插入后的元素数量
<?php
$city = array("bangalore", "delhi");
$count = array_unshift($city, "pune", "kolkata");
echo $count;
输出:
4
返回值4表示插入后数组有4个元素。这个返回值在需要判断插入是否成功、或者需要知道新长度的场景下有用。
五、容易踩的坑
数值键名被重建导致索引丢失。如果原数组的数值键名有业务含义(比如键名是订单ID),array_unshift会把这些键名全部重建,业务含义丢失。这种场景建议用字符串键名,或者改用其他数据结构。
传引用修改原数组。array_unshift直接修改传入的数组。如果业务上需要保留原数组,先复制一份。这个坑在函数参数传递时容易被忽略。
插入大量元素时的性能。array_unshift需要移动所有原有元素,时间复杂度是O(n)。如果数组有几千条以上,且需要频繁在头部插入,性能会明显下降。这种场景建议用SplDoublyLinkedList或SplQueue这类为头部操作优化的数据结构。
返回值和数组本身容易混淆。返回值是元素个数,不是数组。写代码时如果写成$arr=array_unshift($arr,"x");,$arr会变成一个整数,后续操作全部出错。这个错误在新手里比较常见。
多维数组插入不受影响。array_unshift只操作最外层数组的键名,子数组的键名不受影响。但如果子数组是用数字索引的,外层重建键名后,按外层索引访问子数组的逻辑需要同步调整。
六、本节课程知识要点
-
array_unshift在数组开头插入一个或多个元素
-
第一个参数传引用,函数直接修改原数组
-
返回插入后数组的元素个数,类型是整数
-
数值键名会被重建,从0开始连续编号
-
字符串键名保持不变
-
多元素按参数顺序依次插入,最先传的排在最前面
-
需要保留原数组时先复制一份
-
频繁头部插入的大数组建议考虑SplQueue等结构
七、适用场景
这个函数适合以下场景:
-
队列头部插入高优先级任务
-
日志记录中把新记录插到开头
-
面包屑导航中往前追加父级节点
-
临时在列表开头补一条提示项
不适合的场景:
-
数值键名有业务含义的数组,键名会被重建
-
需要保留原数组不变的场景,先复制再操作
-
超大数组频繁头部插入,考虑专用数据结构
-
需要按键名合并数组,用array_merge更合适