一、array_reverse() 反转的是什么
它反转的是元素的顺序,不是键值对应关系,也不是键名本身。默认情况下,反转后数字键会被重新编号,字符串键保持不变。这个默认行为是很多问题的来源,也是 preserve_keys 参数存在的理由。
理解一句话就够了:array_reverse() 把数组从后往前重新排一遍,排完之后键名怎么处理,取决于第二个参数。
二、函数原型与参数
array_reverse(array $array, bool $preserve_keys = false): array
| 参数 | 说明 | 是否必需 |
|---|---|---|
| array | 待反转的数组 | 必需 |
| preserve_keys | 是否保留原键名,默认 false | 可选 |
返回值:反转后的新数组。原数组不受影响。
关于 preserve_keys 的行为,要分键类型来看:
-
数字键:
preserve_keys = false时重新编号为 0、1、2……;true时保留原数字键,但键的顺序跟着元素一起倒过来。 -
字符串键:无论 preserve_keys 传什么,字符串键始终保留,不会丢失也不会重编号。
这个差异是理解这个函数的关键。很多人以为 preserve_keys 对所有键都生效,其实字符串键本来就不会被丢。
三、数字键的两种结果
直接看对比:
<?php
$num = array(100, 200, 300, 400, 500);
$x = array_reverse($num, true);
$y = array_reverse($num);
print_r($x);
print_r($y);
输出:
Array
(
[4] => 500
[3] => 400
[2] => 300
[1] => 200
[0] => 100
)
Array
(
[0] => 500
[1] => 400
[2] => 300
[3] => 200
[4] => 100
)
$x 保留了原键名,500 带着它的 4 号键跑到最前面,键名顺序变成 4、3、2、1、0。$y 重新编号,值倒过来,键名还是 0 到 4。
这里有个容易看错的地方:$x 的键不是"从小到大",而是"从大到小"。因为键跟着元素一起倒过来了,不是重新排序。preserve_keys 保留的是键与值的绑定关系,不是把键名重新排好。
四、混合类型数组的反转
数组里同时有标量和子数组时,反转只作用于最外层:
<?php
$a = array("javatpoint", 2.0, array("PHP", "JAVA"));
$reversed = array_reverse($a);
$preserved = array_reverse($a, true);
print_r($reversed);
print_r($preserved);
输出:
Array
(
[0] => Array
(
[0] => PHP
[1] => JAVA
)
[1] => 2
[2] => javatpoint
)
Array
(
[2] => Array
(
[0] => PHP
[1] => JAVA
)
[1] => 2
[0] => javatpoint
)
嵌套的子数组 ["PHP", "JAVA"] 内部顺序不变,只是它作为整体被移到了外层的最前面。array_reverse() 不递归,这一点和 array_replace_recursive() 那类递归函数不同。需要递归反转时,得自己写递归逻辑。
五、关联数组的反转
字符串键的情况下,preserve_keys 传不传结果一样:
<?php
$lang = array("a" => "PHP", "b" => "JAVA", "c" => "PYTHON");
print_r(array_reverse($lang));
输出:
Array
(
[c] => PYTHON
[b] => JAVA
[a] => PHP
)
键 a、b、c 都保留,只是顺序变成了 c、b、a。键值对应没变,PYTHON 还是挂在 c 上。
六、与相关函数的差异
| 函数 | 作用 | 是否改键名 | 是否递归 |
|---|---|---|---|
| array_reverse() | 反转元素顺序 | 数字键可选重编号 | 否 |
| array_flip() | 键值互换 | 全部变成原值作键 | 否 |
| rsort() / krsort() | 按值或按键排序 | 视函数而定 | 否 |
| strrev() | 反转字符串 | 不适用 | 不适用 |
要区分清楚:
-
array_reverse() 是"倒着排一遍",键值绑定关系整体保留(或数字键重编号)。
-
array_flip() 是"键和值对调",原来的值变成键,原来的键变成值。反转和翻倒是两件事。
-
rsort() 是按值降序排序,会重新分配数字键。排序看的是值的大小,反转看的是元素原本的位置。
个人经验:曾有个需求是"把日志按时间倒序显示",同事第一反应是用 rsort()。但日志数组本身已经按时间排好了,只需要倒过来,用 array_reverse() 一步到位,还不破坏原本的顺序关系。用排序函数是过度处理,还可能因为排序稳定性问题导致同一时间的日志顺序变化。已经有序的数据要倒过来,用反转;无序的数据要变有序,才用排序。
七、项目实例
实例一:新消息置顶
<?php
$messages = ['第一条', '第二条', '第三条', '第四条'];
$latestFirst = array_reverse($messages);
foreach ($latestFirst as $msg) {
echo $msg . "\n";
}
输出:
第四条
第三条
第二条
第一条
消息按时间先后存在数组里,展示时倒过来,新的排在最前。这种场景不需要保留原键名,默认行为就够了。
实例二:保留键名的倒序输出
<?php
function reverseKeepKeys($array) {
return array_reverse($array, true);
}
$lang = array("PHP", "JAVA", "PERL", "PYTHON");
print_r(reverseKeepKeys($lang));
输出:
Array
(
[3] => PYTHON
[2] => PERL
[1] => JAVA
[0] => PHP
)
当元素下标本身有业务含义(比如对应数据库自增 ID 或步骤编号)时,用 preserve_keys = true 保留这个对应关系,避免下游拿错数据。
实例三:分页数据倒序
<?php
$pageData = [
10 => '记录A',
11 => '记录B',
12 => '记录C',
];
$reversed = array_reverse($pageData, true);
print_r($reversed);
输出:
Array
(
[12] => 记录C
[11] => 记录B
[10] => 记录A
)
分页返回的数据如果带固定 ID 作为键,倒序展示时保留键,前端能继续按 ID 做映射。丢键的话,映射关系就断了。
八、踩坑记录
坑一:以为 preserve_keys 对所有键都生效。 字符串键本来就不会被重编号,这个参数主要影响数字键。测试时如果只测关联数组,容易得出"这个参数没用"的错误结论。
坑二:反转后键名顺序是从大到小。 preserve_keys = true 时,数字键跟着元素倒过来,键名不再是从 0 递增。如果下游代码用 for 循环按下标顺序读,会读错。
坑三:多层数组只反转外层。 子数组内部顺序不变。需要递归反转时,得自己封装递归函数。
坑四:和 array_flip() 混用。 想反转顺序却用了 array_flip(),结果键值对调了,数组结构全变。两者语义不同。
坑五:反转后的数组当有序数据用。 如果原数组本身没有明确顺序,反转出来的顺序也没有业务意义。反转只对"原本有顺序"的数组才有价值。
九、本节课程知识要点
-
array_reverse() 反转元素顺序,返回新数组,原数组不变。
-
preserve_keys 默认 false,数字键重新编号;传 true 时数字键保留并跟着元素倒序。
-
字符串键不受 preserve_keys 影响,始终保留。
-
只反转最外层,不递归处理嵌套子数组。
-
与 array_flip() 语义不同,前者反转顺序,后者键值对调。
-
数据本身有序、只需倒过来展示时用反转;数据无序、需要排序时用排序函数。
十、常见问题
问:array_reverse() 会修改原数组吗?
不会。返回的是新数组,原数组保持不变。
问:关联数组反转后键还在吗?
在。字符串键始终保留,键值对应关系不变。
问:数字键保留后为什么顺序是倒的?
因为键跟着元素一起移动,元素倒过来了,键的位置也倒过来了,并没有重新按键名排序。
问:能不能只反转一部分元素?
array_reverse() 不支持区间反转。需要局部反转时,先用 array_slice() 截取,反转后再拼回去。
问:嵌套数组能一次全反转吗?
不能。它只处理最外层。递归反转需要自己写函数,在回调里对子数组再调一次 array_reverse()。