[L1] PHP 索引数组与关联数组的区别及常见操作
一句话结论
PHP 数组统一用有序哈希表实现,索引数组与关联数组只是键的写法不同,底层结构完全一致。
体系讲解
原理:PHP 的数组不是"数组"
传统语言中的数组(如 C 的 int arr[])是一段连续内存,以整数下标做偏移访问。PHP 的数组本质是有序字典(ordered map),同时支持整数键和字符串键,且保持插入顺序。
- 索引数组:键为从 0 开始的连续整数,写法类似传统数组。
- 关联数组:键为字符串,写法类似字典 / map。
两者可以混用,PHP 不做区分——它们是同一数据结构的不同使用方式。
机制:键的自动转换规则
PHP 在赋值时会对键做隐式类型转换:
- 合法整数形式的字符串键(如
"1")自动转为整数键。 - 浮点数键截断为整数(
$a[1.7]等同于$a[1])。 true转为1,false转为0,null转为""。- 未指定键时,取当前最大整数键 + 1。
常用数组函数速查
| 场景 | 函数 | 说明 |
|---|---|---|
| 判断键是否存在 | array_key_exists() | 区分"不存在"和"值为 null" |
| 判断值是否存在 | in_array($v, $arr, true) | 第三参数 true 启用严格比较 |
| 合并数组 | array_merge() | 字符串键覆盖,整数键重新编号 |
| 合并保留键 | + 运算符 | 键冲突时保留左侧,不重新编号 |
| 遍历 | foreach ($arr as $k => $v) | 按插入顺序遍历 |
| 排序 | sort() / asort() / ksort() | 注意是否保留键名 |
结论:对开发的直接影响
- 不需要像其他语言那样在数组和字典之间做选型,PHP 数组"一招通吃"。
- 但也因此容易踩隐式转换的坑:
"0"和0是同一个键,true和1也是。 array_merge()与+的行为差异是高频面试陷阱,必须区分清楚。
考察意图
- 验证候选人是否理解 PHP 数组的本质是有序字典,而非简单的线性结构
- 考察对键的隐式转换规则的了解,这是日常开发中隐蔽 bug 的来源
- 检验对
array_merge()与+等常用函数行为差异的掌握
追问链
array_merge()和+运算符合并数组时有什么区别?简答:
array_merge()遇到字符串键后者覆盖前者,整数键全部保留并重新从 0 编号。+运算符遇到任何重复键都保留左侧(先出现的),整数键不重新编号。$arr = ["1" => "a", 1 => "b"],$arr最终有几个元素?简答:只有 1 个。字符串
"1"会被转为整数键1,后面的赋值覆盖前面的,最终$arr = [1 => "b"]。sort()和asort()有什么区别?什么时候该用哪个?简答:
sort()对值排序并重置键为 0, 1, 2...,适合索引数组。asort()对值排序但保留原始键名,适合关联数组需要保持键值对应关系的场景。如何判断一个数组是索引数组还是关联数组?
简答:PHP 没有内置函数区分。常用判断方式是
array_is_list()(PHP 8.1+),它检测数组键是否为从 0 开始的连续整数。PHP 8.1 之前可用$arr === array_values($arr)近似判断。
易错点
以为
array_merge()和+效果一样:二者对整数键的处理截然不同。array_merge([0 => 'a'], [0 => 'b'])得到['a', 'b'](两个元素,重新编号),而[0 => 'a'] + [0 => 'b']得到['a'](左侧优先,只有一个元素)。忽略键的隐式转换:
$arr["0"]和$arr[0]指向同一个元素。在从数据库取数据(字段值通常是字符串)后用整数做键查找,或反过来,都可能产生意外覆盖。以为
foreach遍历顺序不确定:有其他语言经验的候选人(如 Go 的 map 遍历顺序随机)常做此假设。PHP 数组始终按插入顺序遍历,这是语言规范保证的。
代码示例
<?php
// 索引数组与关联数组的声明
$indexed = ['apple', 'banana', 'cherry']; // 键: 0, 1, 2
$assoc = ['name' => '张三', 'age' => 28]; // 键: name, age
// 键的隐式转换
$mixed = [
0 => 'int_zero',
"0" => 'str_zero', // "0" → 整数 0,覆盖上一行
true => 'bool_true', // true → 1
1.7 => 'float', // 1.7 → 1,覆盖 true 那行
null => 'null_key', // null → ""
];
// 最终: [0 => 'str_zero', 1 => 'float', '' => 'null_key']
echo count($mixed); // 3
// array_merge() vs +
$a = [0 => 'a', 1 => 'b'];
$b = [0 => 'c', 1 => 'd'];
$merged = array_merge($a, $b);
// [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'] — 整数键重编号,全保留
$union = $a + $b;
// [0 => 'a', 1 => 'b'] — 键冲突保留左侧
// PHP 8.1+: array_is_list() 判断是否为索引数组
var_dump(array_is_list([1, 2, 3])); // true
var_dump(array_is_list([0 => 1, 2 => 3])); // false(不连续)
var_dump(array_is_list(['a' => 1])); // false(字符串键)