四:PHP 数组——最强大的数据结构

一、回顾与本篇目标

上一篇你学会了 PHP 的条件判断和循环。你已经能写出有逻辑的程序了——根据条件执行不同的代码,用循环重复处理数据。

这一篇我们要学的是 PHP 中最重要、最强大、最常用的数据结构——数组。在 PHP 中,数组不是 C 语言那种只能存相同类型元素的连续内存块,也不是 Python 中需要区分 list 和 dict 的两种独立类型。PHP 的数组一个顶俩——它同时承担了”列表”和”字典”两种角色,甚至还可以混用。

PHP 的数组功能之强大,在主流编程语言中是罕见的。它内置了排序、过滤、映射、合并、去重等几十个函数,不需要引入任何库就能完成复杂的数据处理。这是 PHP 在 Web 开发中如此高效的重要原因之一。

本篇的目标:

  1. 理解 PHP 数组的两种类型:索引数组和关联数组
  2. 掌握数组的创建、访问、增删改查
  3. 学会 foreach 遍历数组的各种写法
  4. 掌握多维数组的用法
  5. 学会最常用的数组函数:排序、过滤、合并、去重等
  6. 理解 PHP 数组和 JavaScript 数组/Python 列表字典的对应关系

二、PHP 数组的本质

PHP 的数组本质上是一个有序映射——它把映射到。每个数组元素都由一个和一个组成。

如果你不指定键,PHP 会自动分配数字键(从 0 开始递增)——这就是索引数组,相当于 Python 的 list 或 JavaScript 的数组。

如果你指定了字符串键——这就是关联数组,相当于 Python 的 dict 或 JavaScript 的对象。

关键区别:在 JavaScript 中,数组和对象是两种不同的类型。在 Python 中,list 和 dict 是两种不同的类型。在 PHP 中,它们是同一种类型——都是 array

<?php
// 索引数组(PHP 自动分配数字键 0, 1, 2)
$fruits = ["苹果", "香蕉", "橘子"];

// 关联数组(你指定字符串键)
$user = [
    "name" => "张三",
    "age" => 28,
    "city" => "上海"
];

// 用 var_dump 看看它们的实际结构
var_dump($fruits);
// array(3) { [0]=> string(6) "苹果" [1]=> string(6) "香蕉" [2]=> string(6) "橘子" }

var_dump($user);
// array(3) { ["name"]=> string(6) "张三" ["age"]=> int(28) ["city"]=> string(6) "上海" }
?>

看到 var_dump 输出的方括号了吗?索引数组的键是 [0][1][2],关联数组的键是 ["name"]["age"]["city"]结构完全一样,只是键的类型不同。

三、创建数组的多种方式

3.1 用 [] 语法(PHP 5.4+,最推荐)

<?php
// 空数组
$empty = [];

// 索引数组
$numbers = [10, 20, 30, 40, 50];

// 关联数组
$person = [
    "name" => "张三",
    "age" => 28,
    "city" => "上海"
];

// 混合数组(键既有数字也有字符串)
$mixed = [
    "title" => "产品列表",
    10, 20, 30,           // 自动分配键 0, 1, 2
    "total" => 3
];
?>

3.2 用 array() 函数(旧版语法,仍广泛使用)

<?php
$fruits = array("苹果", "香蕉", "橘子");
$user = array(
    "name" => "张三",
    "age" => 28
);
?>

[]array() 完全等价。[] 更简洁,但 array() 在老代码中非常常见,需要能看懂。

3.3 用 range() 创建数字序列

<?php
$nums = range(1, 10);        // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
$odds = range(1, 10, 2);     // [1, 3, 5, 7, 9] —— 第三个参数是步长
$letters = range('a', 'z');  // ['a', 'b', 'c', ..., 'z'] —— 字母也可以
?>

3.4 用 explode() 从字符串创建数组

<?php
$str = "苹果,香蕉,橘子,葡萄";
$fruits = explode(",", $str);
print_r($fruits);
// Array ( [0] => 苹果 [1] => 香蕉 [2] => 橘子 [3] => 葡萄 )
?>

explode(分隔符, 字符串) 把字符串按分隔符切开,返回数组。这是处理 CSV 数据或用户输入时非常常用的函数。

对应的反向操作是 implode(分隔符, 数组),把数组元素拼接成字符串:

<?php
$fruits = ["苹果", "香蕉", "橘子"];
echo implode("、", $fruits);  // 苹果、香蕉、橘子
?>

四、访问和修改数组元素

4.1 用方括号访问

<?php
$fruits = ["苹果", "香蕉", "橘子"];

echo $fruits[0];     // 苹果 —— 第一个元素
echo $fruits[2];     // 橘子 —— 第三个元素

$user = ["name" => "张三", "age" => 28];
echo $user["name"];  // 张三
echo $user["age"];   // 28
?>

注意:PHP 数组的键区分大小写。$user["name"]$user["Name"] 是两个不同的键。

4.2 添加元素

<?php
$fruits = ["苹果", "香蕉"];

// 方式一:用 [] 追加到末尾(自动分配下一个数字键)
$fruits[] = "橘子";           // 现在键是 2

// 方式二:指定键添加(如果键已存在,则覆盖)
$fruits[5] = "葡萄";          // 键 5

// 方式三:关联数组添加
$user = ["name" => "张三"];
$user["age"] = 28;            // 新增键 age
$user["city"] = "上海";       // 新增键 city

print_r($fruits);
// Array ( [0] => 苹果 [1] => 香蕉 [2] => 橘子 [5] => 葡萄 )
?>

注意:当你用 $arr[] = 值 追加时,PHP 会使用当前最大整数键 + 1。上面例子中,添加”橘子”时最大整数键是 1,所以新键是 2。添加”葡萄”时指定了键 5。如果再追加 $fruits[] = "西瓜",键会是 6(而不是 3)。

4.3 修改元素

<?php
$fruits = ["苹果", "香蕉", "橘子"];

$fruits[1] = "草莓";           // 把"香蕉"改成"草莓"
$fruits["new"] = "芒果";       // 添加一个字符串键的元素

print_r($fruits);
// Array ( [0] => 苹果 [1] => 草莓 [2] => 橘子 [new] => 芒果 )
?>

4.4 删除元素

<?php
$fruits = ["苹果", "香蕉", "橘子", "葡萄"];

// 用 unset() 删除指定元素
unset($fruits[1]);             // 删除"香蕉"

print_r($fruits);
// Array ( [0] => 苹果 [2] => 橘子 [3] => 葡萄 )
// 注意:键 1 被删除了,但其他键不会重新编号!

// 用 array_values() 重新索引数组
$fruits = array_values($fruits);
print_r($fruits);
// Array ( [0] => 苹果 [1] => 橘子 [2] => 葡萄 )
?>

unset 不会重新索引数组。删除键 1 后,数组的键变成了 0、2、3。如果需要重新按 0、1、2 编号,用 array_values()

4.5 检查键是否存在

<?php
$user = ["name" => "张三", "age" => 28];

// 用 array_key_exists() 检查键是否存在
var_dump(array_key_exists("name", $user));   // true
var_dump(array_key_exists("email", $user));  // false

// 用 isset() 检查键是否存在且值不为 NULL
var_dump(isset($user["name"]));   // true
var_dump(isset($user["email"]));  // false
?>

array_key_exists() 和 isset() 的区别:如果键存在但值为 NULLarray_key_exists() 返回 trueisset() 返回 false

4.6 检查值是否存在

<?php
$fruits = ["苹果", "香蕉", "橘子"];

var_dump(in_array("香蕉", $fruits));   // true
var_dump(in_array("西瓜", $fruits));   // false
?>

五、遍历数组

5.1 foreach 遍历值

<?php
$fruits = ["苹果", "香蕉", "橘子"];

foreach ($fruits as $fruit) {
    echo $fruit . "<br>";
}
?>

5.2 foreach 同时获取键和值

<?php
$user = ["name" => "张三", "age" => 28, "city" => "上海"];

foreach ($user as $key => $value) {
    echo "{$key}: {$value}<br>";
}
// name: 张三
// age: 28
// city: 上海
?>

5.3 foreach 遍历时修改数组

遍历值时修改——不会影响原数组:

<?php
$nums = [1, 2, 3];

foreach ($nums as $num) {
    $num = $num * 10;  // 修改的是 $num 副本,不影响原数组
}
print_r($nums);  // Array ( [0] => 1 [1] => 2 [2] => 3 ) —— 没变
?>

遍历时通过引用修改——会影响原数组:

<?php
$nums = [1, 2, 3];

foreach ($nums as &$num) {  // 注意 & 符号——引用传递
    $num = $num * 10;
}
unset($num);  // 重要!遍历结束后取消引用,防止后续意外修改

print_r($nums);  // Array ( [0] => 10 [1] => 20 [2] => 30 ) —— 变了
?>

foreach 引用遍历后必须 unset 引用变量。如果不 unset($num)$num 仍然指向数组最后一个元素,后续代码中对 $num 的任何操作都会意外修改那个元素。

5.4 用 for 遍历索引数组

<?php
$fruits = ["苹果", "香蕉", "橘子"];
$count = count($fruits);  // 获取数组长度

for ($i = 0; $i < $count; $i++) {
    echo "第 " . ($i + 1) . " 个水果:{$fruits[$i]}<br>";
}
?>

count($arr) 返回数组的元素个数,相当于 JavaScript 的 arr.length 或 Python 的 len(arr)

六、多维数组

数组的元素本身也可以是数组——这就是多维数组。最常见的是二维数组,用来存储表格数据:

<?php
$students = [
    ["name" => "张三", "age" => 20, "score" => 85.5],
    ["name" => "李四", "age" => 21, "score" => 92.0],
    ["name" => "王五", "age" => 19, "score" => 78.5],
    ["name" => "赵六", "age" => 22, "score" => 95.0],
];

// 访问二维数组元素
echo $students[0]["name"];    // 张三
echo $students[2]["score"];   // 78.5

// 遍历二维数组
foreach ($students as $index => $student) {
    echo "学生 " . ($index + 1) . ":";
    echo "{$student["name"]},";
    echo "{$student["age"]}岁,";
    echo "{$student["score"]}分<br>";
}
?>

七、常用数组函数

PHP 内置了超过 80 个数组处理函数。下面是最常用的十几个,按功能分类。

7.1 统计类

<?php
$nums = [10, 20, 30, 40, 50];

echo count($nums);           // 5 —— 元素个数
echo sizeof($nums);          // 5 —— count 的别名
echo array_sum($nums);       // 150 —— 所有元素的和
echo array_product($nums);   // 12000000 —— 所有元素的乘积
echo max($nums);             // 50 —— 最大值
echo min($nums);             // 10 —— 最小值
?>

7.2 查找类

<?php
$fruits = ["苹果", "香蕉", "橘子", "香蕉"];

// 检查值是否存在
var_dump(in_array("香蕉", $fruits));       // true

// 查找值的位置(返回第一个匹配的键)
echo array_search("香蕉", $fruits);        // 1

// 获取所有键
print_r(array_keys($fruits));              // [0, 1, 2, 3]

// 获取所有值
print_r(array_values($fruits));            // ["苹果", "香蕉", "橘子", "香蕉"]

// 检查键是否存在
var_dump(array_key_exists(2, $fruits));    // true
?>

7.3 增删改类

<?php
$arr = ["a" => "苹果", "b" => "香蕉"];

// 在开头添加
array_unshift($arr, "草莓");
// 在末尾添加
array_push($arr, "橘子", "葡萄");
// 从开头删除
$first = array_shift($arr);
// 从末尾删除
$last = array_pop($arr);

// 用 print_r 而不是 var_dump 获得更简洁的输出
print_r($arr);

// 删除重复值
$with_dups = [1, 2, 2, 3, 3, 3, 4];
$unique = array_unique($with_dups);
print_r($unique);  // [1, 2, 3, 4]

// 反转数组
$reversed = array_reverse([1, 2, 3]);
print_r($reversed);  // [3, 2, 1]

// 随机抽取一个或多个元素
$random_key = array_rand(["a" => 1, "b" => 2, "c" => 3]);  // 随机返回一个键
$random_keys = array_rand(["a" => 1, "b" => 2, "c" => 3], 2);  // 返回两个键的数组

// 打乱数组
$nums = [1, 2, 3, 4, 5];
shuffle($nums);
print_r($nums);  // 顺序随机(注意:键会被重新索引)
?>

7.4 排序类

PHP 的排序函数非常多,按”是否保留键”和”升序/降序”组合出多种变体:

函数 排序方式 是否保留键 说明
sort($arr) 升序 ❌ 重新索引 最常用
rsort($arr) 降序 ❌ 重新索引 reverse sort
asort($arr) 升序 ✅ 保留键 关联数组排序用这个
arsort($arr) 降序 ✅ 保留键 关联数组降序
ksort($arr) 按键升序 按 key 排序
krsort($arr) 按键降序 按 key 反向排序
<?php
$fruits = ["橘子", "苹果", "香蕉"];

sort($fruits);
print_r($fruits);  // [0] => 苹果 [1] => 橘子 [2] => 香蕉 —— 升序排列

$scores = ["张三" => 85, "李四" => 92, "王五" => 78];
asort($scores);    // 按值升序,保留键
print_r($scores);  // [王五] => 78 [张三] => 85 [李四] => 92
?>

7.5 过滤和映射类

<?php
$nums = [10, 15, 20, 25, 30, 35, 40];

// array_filter:筛选出满足条件的元素
$even = array_filter($nums, function($n) {
    return $n % 2 == 0;  // 保留偶数
});
print_r($even);  // [10, 20, 30, 40]

// array_map:对每个元素应用函数
$doubled = array_map(function($n) {
    return $n * 2;
}, $nums);
print_r($doubled);  // [20, 30, 40, 50, 60, 70, 80]

// 只取某一列(二维数组中非常常用)
$students = [
    ["name" => "张三", "score" => 85],
    ["name" => "李四", "score" => 92],
    ["name" => "王五", "score" => 78],
];
$names = array_column($students, "name");
print_r($names);  // ["张三", "李四", "王五"]
?>

7.6 合并和拆分

<?php
// array_merge:合并两个或多个数组
$arr1 = ["a" => "苹果", "b" => "香蕉"];
$arr2 = ["b" => "蓝莓", "c" => "橘子"];
$merged = array_merge($arr1, $arr2);
print_r($merged);
// ["a" => "苹果", "b" => "蓝莓", "c" => "橘子"]
// 注意:相同的字符串键,后面的覆盖前面的

$arr3 = [10, 20];
$arr4 = [30, 40];
$merged2 = array_merge($arr3, $arr4);
print_r($merged2);  // [10, 20, 30, 40] —— 数字键会重新索引

// array_slice:截取数组的一部分
$nums = [10, 20, 30, 40, 50];
$slice = array_slice($nums, 1, 3);  // 从索引 1 开始,取 3 个
print_r($slice);  // [20, 30, 40]

// array_chunk:把数组分成多个小块
$chunks = array_chunk([1, 2, 3, 4, 5, 6, 7], 3);
print_r($chunks);  // [[1,2,3], [4,5,6], [7]]
?>

7.7 集合运算

<?php
$a = [1, 2, 3, 4];
$b = [3, 4, 5, 6];

// 差集:在 $a 中但不在 $b 中
print_r(array_diff($a, $b));  // [1, 2]

// 交集:同时在 $a 和 $b 中
print_r(array_intersect($a, $b));  // [3, 4]
?>

八、PHP 数组和 JavaScript/Python 的对照表

操作 PHP JavaScript Python
创建列表 [1, 2, 3] [1, 2, 3] [1, 2, 3]
创建字典/对象 ["a" => 1] {a: 1} {"a": 1}
访问元素 $arr[0]$arr["key"] arr[0]obj.key arr[0]d["key"]
追加到末尾 $arr[] = 值 arr.push(val) arr.append(val)
删除末尾 array_pop($arr) arr.pop() arr.pop()
长度 count($arr) arr.length len(arr)
检查值存在 in_array(val, $arr) arr.includes(val) val in arr
合并 array_merge($a, $b) [...a, ...b] a + b(list)或 {**a, **b}(dict)
过滤 array_filter($arr, fn) arr.filter(fn) filter(fn, arr) 或列表推导式
映射 array_map(fn, $arr) arr.map(fn) map(fn, arr) 或列表推导式
排序 sort($arr) arr.sort() arr.sort()sorted(arr)

九、综合演示:学生成绩管理系统

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>学生成绩管理</title>
    <style>
        body { font-family: sans-serif; padding: 40px; background: #f0f4f8; }
        .container { max-width: 700px; margin: 0 auto; background: white; padding: 30px; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #e0e0e0; }
        th { background: #4a90d9; color: white; }
        .excellent { color: #27ae60; font-weight: bold; }
        .good { color: #2980b9; }
        .pass { color: #f39c12; }
        .fail { color: #e74c3c; font-weight: bold; }
    </style>
</head>
<body>

<div class="container">
    <h2>📊 学生成绩管理系统</h2>

<?php
// 学生数据(通常来自数据库,这里用数组模拟)
$students = [
    ["name" => "张三", "math" => 85, "english" => 78, "chinese" => 92],
    ["name" => "李四", "math" => 92, "english" => 88, "chinese" => 95],
    ["name" => "王五", "math" => 58, "english" => 72, "chinese" => 65],
    ["name" => "赵六", "math" => 95, "english" => 90, "chinese" => 88],
    ["name" => "孙七", "math" => 45, "english" => 55, "chinese" => 60],
];

// 计算每个学生的总分和平均分
foreach ($students as &$student) {
    $student["total"] = $student["math"] + $student["english"] + $student["chinese"];
    $student["average"] = round($student["total"] / 3, 1);

    // 判断等级
    if ($student["average"] >= 90) {
        $student["grade"] = "优秀";
        $student["class"] = "excellent";
    } elseif ($student["average"] >= 80) {
        $student["grade"] = "良好";
        $student["class"] = "good";
    } elseif ($student["average"] >= 60) {
        $student["grade"] = "及格";
        $student["class"] = "pass";
    } else {
        $student["grade"] = "不及格";
        $student["class"] = "fail";
    }
}
unset($student);  // 取消引用

// 按总分降序排列
usort($students, function($a, $b) {
    return $b["total"] - $a["total"];
});
?>

    <table>
        <thead>
            <tr>
                <th>排名</th>
                <th>姓名</th>
                <th>数学</th>
                <th>英语</th>
                <th>语文</th>
                <th>总分</th>
                <th>平均分</th>
                <th>等级</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($students as $index => $s): ?>
                <tr>
                    <td><?= $index + 1 ?></td>
                    <td><?= $s["name"] ?></td>
                    <td><?= $s["math"] ?></td>
                    <td><?= $s["english"] ?></td>
                    <td><?= $s["chinese"] ?></td>
                    <td><?= $s["total"] ?></td>
                    <td><?= $s["average"] ?></td>
                    <td class="<?= $s["class"] ?>"><?= $s["grade"] ?></td>
                </tr>
            <?php endforeach; ?>
        </tbody>
    </table>

    <?php
    // 班级统计
    $all_scores = array_merge(
        array_column($students, "math"),
        array_column($students, "english"),
        array_column($students, "chinese")
    );

    $class_avg = round(array_sum($all_scores) / count($all_scores), 1);
    $class_max = max($all_scores);
    $class_min = min($all_scores);
    $pass_count = count(array_filter($all_scores, function($s) { return $s >= 60; }));
    $pass_rate = round($pass_count / count($all_scores) * 100, 1);
    ?>

    <div style="margin-top: 30px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
        <h3>班级统计</h3>
        <p>班级平均分:<strong><?= $class_avg ?></strong></p>
        <p>最高分:<strong><?= $class_max ?></strong> | 最低分:<strong><?= $class_min ?></strong></p>
        <p>及格率:<strong><?= $pass_rate ?>%</strong>(<?= $pass_count ?>/<?= count($all_scores) ?>)</p>
    </div>
</div>

</body>
</html>

代码解析:

  • &$student:引用传递,让 foreach 循环内部修改数组元素时能直接修改原数组。循环结束后必须 unset($student)
  • usort($arr, fn):用自定义函数排序。这里用匿名函数按总分降序排列。$b["total"] - $a["total"] 为正时 $b 排在前面(降序)。
  • array_merge():把三个科目的分数合并成一个大数组,用于班级统计。
  • array_column():提取二维数组中某一列的所有值。
  • round($val, 1):四舍五入保留一位小数。

十、本篇动手练习

练习 1:购物车计算

新建 practice4-1.php,创建一个购物车数组,每个商品包含 namepricequantity。计算总金额、平均单价、最贵和最便宜的商品。用 foreach 遍历输出购物清单。

练习 2:单词频率统计

新建 practice4-2.php,给定一段英文文本,用 explode() 把文本拆成单词数组,用关联数组统计每个单词出现的次数,按出现次数降序排列输出。

练习 3:数组函数练习

新建 practice4-3.php,创建两个数组,分别测试以下函数并输出结果:array_mergearray_diffarray_intersectarray_filter(筛选出大于 50 的值)、array_map(每个值乘以 1.1)。

练习 4:学生管理系统加强版

修改第九节的综合演示代码:增加”删除成绩最低的学生”功能(用 usort 排序后用 array_shift 删除);增加”按单科成绩筛选”功能(用 array_filter 筛选数学大于 80 分的学生)。

十一、本篇小结

这一篇你系统学习了 PHP 最强大的数据结构——数组:

  • PHP 数组的本质:有序映射。同一个 array 类型同时承担”列表”和”字典”两种角色。
  • 创建数组[] 语法(推荐)、array() 函数、range() 创建序列、explode() 从字符串创建。
  • 访问和修改:方括号访问、[] 追加、unset() 删除、array_values() 重新索引。
  • 遍历数组foreach ($arr as $val) 遍历值、foreach ($arr as $key => $val) 同时获取键和值。引用遍历后必须 unset
  • 多维数组:数组的元素也是数组,用多重方括号访问。
  • 常用数组函数:统计类(countsummax)、查找类(in_arrayarray_search)、排序类(sortasortksort)、过滤映射类(array_filterarray_maparray_column)、合并拆分(array_mergearray_slice)。

PHP 的数组功能之多,一篇写不完。但这篇覆盖了日常开发中 90% 的数组操作。把这些函数练熟,你处理任何表格数据、JSON 数据、数据库查询结果都会游刃有余。

下一篇预告

下一篇——《PHP 函数——封装可复用的代码》:定义和调用函数、参数传递(传值和传引用)、默认参数、可变参数、返回值、匿名函数和闭包、变量作用域、常用内置函数一览。

PHP 零基础入门,每周更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容