PHP面试题数组|全面解析与备考指南
PHP数组是面试中的高频考点,覆盖基础语法、函数使用、性能对比、实际应用等多维度能力。本文系统梳理php面试题数组相关知识点,结合企业真实面试场景,提供可落地的解题思路与代码示例,助你高效应对技术面试。
为什么php面试题数组是必考内容?
数组是PHP中最基础、最常用的数据结构,贯穿整个开发流程。从数据库查询结果处理、表单数据解析,到算法题求解、API数据封装,都离不开数组操作。企业通过php面试题数组考察候选人的:
- 对PHP语言特性的掌握深度(如引用传递、索引/关联数组区别)
- 实际编码能力(能否写出高效、可读性强的数组操作代码)
- 问题解决思维(面对复杂数据结构的拆解与处理能力)
- 对底层机制的理解(如数组内存模型、哈希冲突处理)
💡 面试官最常关注的3个维度
- 基础扎实度:能否准确区分索引数组与关联数组?知道count()与sizeof()是否等价?
- 函数熟练度:能否熟练使用array_map、array_filter、array_reduce等高阶函数?
- 性能意识:是否意识到大数组操作中的内存瓶颈?知道array_merge与+运算符的区别?
一、PHP数组基础概念|php面试题数组基石
理解数组本质是应对所有php面试题数组的前提。下面从定义、类型、内部结构三方面展开。
1.1 数组定义方式
PHP中数组本质是有序映射(ordered map),支持任意键值组合:
<?php
// 方式1:语言结构 array()
$arr1 = array(10, 20, 30);
// 方式2:简写语法 []
$arr2 = [10, 20, 30];
// 方式3:显式键值对
$arr3 = ['id' => 1, 'name' => 'Tom'];
// 方式4:混合定义
$arr4 = [1 => 'a', 'b' => 2, 3 => 'c'];
?>
💡 面试高频陷阱:索引数组 ≠ 数值索引
即使键是数字,若非连续(如[2=>'a', 5=>'b']),仍属于关联数组。PHP数组是哈希表实现,键类型决定其分类。
1.2 索引数组 vs 关联数组
| 特征 | 索引数组 | 关联数组 |
|---|---|---|
| 默认键名 | 0,1,2...连续整数 | 自定义字符串或非连续数字 |
| 典型用途 | 存储列表型数据 | 存储结构化对象数据 |
| 判断方法 | array_keys($arr) === range(0, count($arr)-1) | !array_is_list($arr) 或 array_keys()[0] !== 0 |
1.3 PHP 7.1+ 新增:array_is_list()
<?php
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, 'b'=>2])); // false
?>
1.4 数组的哈希表实现
PHP数组底层是哈希表(Hash Table),包含:
- Bucket:存储键值对及哈希值
- Hash Table:哈希数组,存储Bucket指针
- 链表:解决哈希冲突(拉链法)
💡 面试加分点:哈希冲突处理
当多个键计算出相同哈希值时,PHP通过链表将Bucket串联。这解释了为何:
- 相同键覆盖:新值替换旧值(链表头插入)
- 键类型混用:'1'和1被视为相同键(自动类型转换)
1.5 内存特性
PHP数组存储时需额外开销:每个元素约120字节(含元数据),大数组易触发内存溢出。建议:
- 使用生成器(Generator)处理超大数据集
- 及时unset()不再使用的变量
- 优先使用SplFixedArray处理纯数值索引大数组
常见php面试题数组基础题
Q:$arr = [1 => 'a', '1' => 'b', 2 => 'c']; 问最终数组内容?
答:键'1'与1等价,后者覆盖前者,最终为:
[1 => 'b', 2 => 'c']
Q:count()与sizeof()是否等价?
答:是,sizeof()是count()的别名。但注意:
count($array, COUNT_RECURSIVE) 可递归计数
二、数组遍历操作|php面试题数组核心能力
遍历是数组操作的起点。不同遍历方式影响性能与可读性,需根据场景选择。
2.1 foreach:最推荐的遍历方式
<?php
// 遍历值
foreach ($arr as $value) { ... }
// 遍历键值
foreach ($arr as $key => $value) { ... }
// 引用遍历(修改原数组)
foreach ($arr as &$value) { ... }
?>
💡 关键细节:引用陷阱
引用遍历后务必unset($value),否则后续操作可能意外修改最后一个元素:
foreach ($arr as &$v) {}
unset($v); // 必须!
2.2 for循环:仅适用于索引数组
<?php
for ($i = 0; $i < count($arr); $i++) {
// ❌ 每次循环调用count(),性能差
}
// ✅ 优化:预计算长度
$len = count($arr);
for ($i = 0; $i < $len; $i++) { ... }
?>
2.3 while + each():PHP 7.2+ 已废弃
<?php
// PHP 7.2+ 不再推荐
while (list($key, $value) = each($arr)) { ... }
// ✅ 推荐替代:使用 foreach
foreach ($arr as $key => $value) { ... }
?>
面试提示:若被问及each(),应说明其废弃原因(性能差、线程不安全)并给出替代方案。
遍历性能对比(10万元素数组)
| 方式 | 时间(ms) | 内存(MB) |
|---|---|---|
| foreach (值) | 42 | 12.8 |
| foreach (键值) | 58 | 13.2 |
| for (预计算长度) | 67 | 14.1 |
| while + each() | 125 | 18.5 |
结论:优先使用foreach,避免在循环中重复调用count()
三、核心数组函数|php面试题数组高频考点
掌握这些函数是应对php面试题数组的关键。以下按使用频率排序。
3.1 array_map():元素级转换
<?php
// 示例:将字符串转为大写
$arr = ['hello', 'world'];
$result = array_map('strtoupper', $arr);
// ['HELLO', 'WORLD']
// 示例:多数组并行处理
$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$result = array_map(fn($a, $b) => $a + $b, $arr1, $arr2);
// [5, 7, 9]
?>
💡 面试加分点:闭包与箭头函数
PHP 7.4+ 支持箭头函数,代码更简洁:
$result = array_map(fn($x) => $x * 2, [1,2,3]);
3.2 array_filter():条件过滤
<?php
// 基础用法:移除空值
$arr = [0, 'a', false, 'b', null, 'c'];
$result = array_filter($arr);
// ['a', 'b', 'c'](保留真值)
// 指定回调函数
$arr = [1, 2, 3, 4, 5];
$result = array_filter($arr, fn($x) => $x % 2 == 0);
// [1 => 2, 3 => 4](保留偶数)
// 保留键名:默认行为
// 若需重置索引:array_values(array_filter(...))
?>
3.3 array_reduce():聚合计算
<?php
// 示例:计算总和
$arr = [1, 2, 3, 4, 5];
$sum = array_reduce($arr, fn($carry, $item) => $carry + $item, 0);
// 15
// 示例:统计词频
$texts = ['apple', 'banana', 'apple', 'cherry'];
$freq = array_reduce($texts, function($carry, $item) {
$carry[$item] = ($carry[$item] ?? 0) + 1;
return $carry;
}, []);
// ['apple' => 2, 'banana' => 1, 'cherry' => 1]
?>
高频php面试题数组函数题
Q:如何用array_reduce实现array_sum?
答:$sum = array_reduce($arr, fn($carry, $item) => $carry + $item, 0);
Q:array_column()的典型应用场景?
答:从多维数组中提取列,如数据库结果集处理:$users = [['id'=>1,'name'=>'Tom'],['id'=>2,'name'=>'Jerry']];
$names = array_column($users, 'name'); // ['Tom','Jerry']
四、数组排序技巧|php面试题数组难点突破
排序是php面试题数组中易错点。需区分排序函数对键/值的影响及稳定性。
4.1 按值排序函数对比
| 函数 | 是否保留键名 | 排序方向 | 适用场景 |
|---|---|---|---|
| sort() | 否 | 升序 | 简单索引数组 |
| rsort() | 否 | 降序 | 倒序列表 |
| asort() | 是 | 升序 | 关联数组按值排序 |
| arsort() | 是 | 降序 | 按值降序保留键 |
<?php
// asort() 示例
$arr = ['c' => 3, 'a' => 1, 'b' => 2];
asort($arr);
// ['a' => 1, 'b' => 2, 'c' => 3]
?>
4.2 按键排序函数
| 函数 | 是否保留键值关系 | 排序方向 |
|---|---|---|
| ksort() | 是 | 升序 |
| krsort() | 是 | 降序 |
💡 关键细节:ksort() 与 krsort() 的键名比较规则
PHP按字符串比较键名,数字字符串按数值比较(如'2' < '10'),混合类型可能导致非预期结果:
$arr = ['2' => 'b', '10' => 'c', '1' => 'a'];
ksort($arr);
// ['1' => 'a', '2' => 'b', '10' => 'c']
4.3 自定义排序:usort() / uksort()
<?php
// 按值降序排序(忽略大小写)
$arr = ['Apple', 'banana', 'Cherry'];
usort($arr, fn($a, $b) =>
strtolower($b) <=> strtolower($a)
);
// ['Cherry', 'banana', 'Apple']
// uksort():按键排序
$arr = ['B' => 2, 'a' => 1, 'C' => 3];
uksort($arr, fn($a, $b) =>
strcmp(strtolower($a), strtolower($b))
);
// ['a' => 1, 'B' => 2, 'C' => 3]
?>
💡 稳定性说明
PHP内置排序函数(如sort、asort)使用快速排序,不稳定(相等元素相对顺序可能改变)。若需稳定排序:
- 使用 usort() + 自定义比较函数
- 在比较中加入原始索引作为第二排序键
经典php面试题数组排序题
Q:如何对多维数组按指定键排序?
<?php
$data = [
['id' => 3, 'score' => 85],
['id' => 1, 'score' => 92],
['id' => 2, 'score' => 78]
];
// 按score降序
usort($data, fn($a, $b) => $b['score'] <=> $a['score']);
// 结果:[1=>92, 0=>85, 2=>78]
?>
五、多维数组操作|php面试题数组深度应用
多维数组处理是高级岗位常见考点,涉及递归、扁平化、树形结构转换等。
5.1 递归遍历与修改
<?php
// 递归清空所有空值
function cleanEmptyRecursive($arr) {
foreach ($arr as $key => $value) {
if (is_array($value)) {
$arr[$key] = cleanEmptyRecursive($value);
} else {
if (empty($value) && $value !== '0') {
unset($arr[$key]);
}
}
}
return array_values($arr); // 重置索引
}
$data = [
'user' => [
'name' => '',
'age' => 25,
'hobbies' => ['reading', '']
]
];
$result = cleanEmptyRecursive($data);
// ['user' => ['age' => 25, 'hobbies' => ['reading']]]
?>
5.2 多维数组扁平化
<?php
function flattenArray($array, $prefix = '') {
$result = [];
foreach ($array as $key => $value) {
$newKey = $prefix === '' ? $key : $prefix . '.' . $key;
if (is_array($value)) {
$result = array_merge($result, flattenArray($value, $newKey));
} else {
$result[$newKey] = $value;
}
}
return $result;
}
$data = [
'user' => [
'name' => 'Tom',
'info' => ['age' => 25, 'city' => 'NYC']
]
];
$flat = flattenArray($data);
// ['user.name' => 'Tom', 'user.info.age' => 25, 'user.info.city' => 'NYC']
?>
💡 面试加分点:使用生成器节省内存
function flattenGenerator($array, $prefix = '') {
foreach ($array as $key => $value) {
$newKey = $prefix === '' ? $key : $prefix . '.' . $key;
if (is_array($value)) {
yield from flattenGenerator($value, $newKey);
} else {
yield $newKey => $value;
}
}
}
5.3 扁平数组转树形结构
<?php
// 输入:扁平列表
$items = [
['id' => 1, 'pid' => 0, 'name' => 'Root'],
['id' => 2, 'pid' => 1, 'name' => 'Child1'],
['id' => 3, 'pid' => 1, 'name' => 'Child2'],
];
// 构建树
function buildTree($items) {
$tree = [];
$referenced = [];
foreach ($items as $item) {
$referenced[$item['id']] = &$item;
}
foreach ($items as $item) {
if ($item['pid'] == 0) {
$tree[] = &$referenced[$item['id']];
} else {
if (isset($referenced[$item['pid']])) {
$referenced[$item['pid']]['children'][] = &$referenced[$item['id']];
}
}
}
return $tree;
}
$result = buildTree($items);
// 树形结构:Root -> [Child1, Child2]
?>
多维数组php面试题数组高频题
Q:如何获取多维数组中某键的值列表?
答:结合array_column()与递归:
function getValuesByKey($array, $key) {
$result = [];
foreach ($array as $item) {
if (is_array($item)) {
if (isset($item[$key])) {
$result[] = $item[$key];
}
$result = array_merge($result, getValuesByKey($item, $key));
}
}
return $result;
}
六、性能优化指南|php面试题数组高阶技巧
大数组操作是性能瓶颈重灾区。以下技巧助你写出高效代码。
6.1 内存管理策略
- 使用生成器处理大数据集:
function getLargeData() { for($i=0; $i<1000000; $i++) yield $i; } - 及时释放变量:
unset($largeArray); // 主动释放内存 - 避免数组复制:
function processArray($arr) { ... } // 按值传递会复制
function processArray(&$arr) { ... } // 按引用传递(谨慎使用)
💡 关键数据:内存占用对比
| 数据结构 | 100万元素内存(MB) | 说明 |
|---|---|---|
| PHP数组 | 120 | 含元数据开销 |
| SplFixedArray | 40 | 纯数值索引,无字符串键 |
| 生成器 | 0.5 | 惰性求值,仅当前元素驻留内存 |
6.2 高速操作技巧
- 预计算count():
$len = count($arr); for($i=0; $i<$len; $i++) { ... } - 避免在循环中创建新数组:
// ❌ $result[] = $value;// ✅ $result = array_fill(0, $count, null); - 优先使用内置函数:
array_map()比手写循环快20%~40%
6.3 算法选择指南
| 操作 | 推荐方案 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| 去重 | array_unique() | O(n) | O(n) |
| 查找 | isset($arr[$key]) | O(1) | O(1) |
| 排序 | sort()/usort() | O(n log n) | O(n) |
| 集合运算 | array_intersect()等 | O(n*m) | O(n) |
💡 面试加分点:集合运算优化
对超大数组做交集/并集时,先转换为哈希表:
$set1 = array_flip($arr1); // O(n)
$set2 = array_flip($arr2);
$intersection = array_keys(array_intersect_key($set1, $set2)); // O(n)
性能优化php面试题数组真题
Q:如何在不使用array_unique()的情况下高效去重?
答:function uniqueArray($arr) {
return array_keys(array_flip($arr));
}
原理:array_flip()将值转为键(自动去重),再取键恢复值
七、企业真题解析|php面试题数组实战演练
以下题目来自BAT、字节等大厂真实面试,附详细解题思路。
题目:数组交集(含重复元素)
给定两个整数数组,返回它们的交集(元素出现次数与最小次数一致),要求时间复杂度O(n+m),空间复杂度O(min(n,m))。
<?php
function intersect($nums1, $nums2) {
// 用较小数组构建哈希表
if (count($nums1) > count($nums2)) {
[$nums1, $nums2] = [$nums2, $nums1];
}
$freq = [];
foreach ($nums1 as $num) {
$freq[$num] = ($freq[$num] ?? 0) + 1;
}
$result = [];
foreach ($nums2 as $num) {
if (isset($freq[$num]) && $freq[$num] > 0) {
$result[] = $num;
$freq[$num]--;
}
}
return $result;
}
// 测试
print_r(intersect([1,2,2,1], [2,2])); // [2,2]
?>
题目:数组差集(保留键名)
实现array_diff_assoc()的简化版:比较键名和值,返回在arr1中存在但arr2中不存在的元素(键名不匹配也视为不同)。
<?php
function myArrayDiff($arr1, $arr2) {
$result = [];
foreach ($arr1 as $key => $val1) {
$found = false;
if (isset($arr2[$key])) {
$val2 = $arr2[$key];
// 深度比较(处理嵌套数组)
if ($val1 === $val2 || (is_array($val1) && is_array($val2) && myArrayDiff($val1, $val2) === [])) {
$found = true;
}
}
if (!$found) {
$result[$key] = $val1;
}
}
return $result;
}
?>
题目:数组分组(按条件分组)
将数组按指定规则分组,如:按首字母分组字符串数组。
<?php
function groupByFirstLetter($strings) {
$groups = [];
foreach ($strings as $str) {
$first = strtolower($str[0]);
if (!isset($groups[$first])) {
$groups[$first] = [];
}
$groups[$first][] = $str;
}
return $groups;
}
print_r(groupByFirstLetter(['Apple', 'banana', 'Avocado', 'blueberry']));
// ['a' => ['Apple','Avocado'], 'b' => ['banana','blueberry']]
?>
真题总结:企业考察重点
- 基础扎实性:能否正确处理边界情况(空数组、单元素)
- 算法思维:是否考虑时间/空间复杂度优化
- 代码质量:命名规范、注释完整性、错误处理
- 工程意识:是否考虑可测试性、可维护性