PHP数组
在 PHP 中,数组 (Array)是一种用于存储多个值的复合数据类型。数组可以包含多个元素,每个元素可以是一个变量值,这些元素通过 索引 或 键 来访问。PHP 数组是非常灵活的,可以存储任意类型的数据,包括整数、字符串、对象等,甚至可以包含其他数组。
PHP 中的数组是 关联数组 和 索引数组 的结合体,它既可以通过数字索引来访问元素,也可以通过字符串键来访问元素。因此,PHP 数组可以视为 键值对集合。
一,数组的基本特点
1. 混合类型支持
-
键和值可以是任意类型:
-
键(Key) :支持整数(
int
)和字符串(string
),其他类型(如浮点数、布尔值)会被自动转换。php$arr = [ 1 => "a", // 整数键 "2" => "b", // 字符串键被隐式转换为整数键 2 true => "c", // 布尔键 true 转为 1 3.5 => "d" // 浮点数键 3.5 被截断为 3 ]; // 结果:array(1 => 'c', 2 => 'b', 3 => 'd')
-
值(Value):可以是任意数据类型(标量、数组、对象等)。
-
2. 有序性与哈希表实现
-
底层实现为哈希表 :PHP 数组本质上是有序字典(Ordered Map),通过哈希表(Hash Table)实现,结合了列表(List)和字典(Dictionary)的特性。
-
保留插入顺序:元素的遍历顺序与插入顺序一致(PHP 7+ 严格保证)。
php$arr = ["a" => 1, "b" => 2]; $arr["c"] = 3; foreach ($arr as $k => $v) { echo "$k => $v\n"; // 输出顺序:a => 1, b => 2, c => 3 }
3. 动态扩容
-
无需预定义大小:数组长度可动态增长,无需手动管理内存。
php$arr = []; $arr[] = 1; // 自动分配键 0 $arr[] = 2; // 自动分配键 1 // 结果:array(0 => 1, 1 => 2)
4. 支持关联数组和索引数组
-
关联数组:使用字符串或自定义整数作为键。
php$user = ["name" => "Alice", "age" => 30];
-
索引数组:使用连续整数键(从 0 开始),行为类似传统列表。
php$list = ["apple", "banana", "cherry"];
5. 丰富的内置函数
PHP 提供了大量数组操作函数,例如:
-
array_merge()
:合并多个数组(覆盖重复键)。 -
array_push()
/array_pop()
:栈操作。 -
array_key_exists()
:检查键是否存在。 -
array_map()
/array_filter()
:高阶函数处理。php$nums = [1, 2, 3]; $squared = array_map(fn($n) => $n * $n, $nums); // [1, 4, 9]
6. 引用传递与拷贝
-
赋值默认是值拷贝:直接赋值会复制整个数组。
-
引用赋值 :使用
&
可创建数组的引用。php$a = [1, 2]; $b = $a; // 值拷贝 $b[0] = 99; // $a 不变 $c = &$a; // 引用赋值 $c[0] = 100; // $a[0] 变为 100
7. 可模拟其他数据结构
PHP 数组可以灵活实现多种数据结构:
-
栈(Stack) :
array_push()
和array_pop()
。 -
队列(Queue) :
array_push()
和array_shift()
。 -
集合(Set):通过键的唯一性模拟。
php$set = ["apple" => true, "banana" => true]; if (isset($set["apple"])) { /* 存在 */ }
8. 性能与内存
- 哈希表的高效查找:键的查找时间复杂度接近 O(1)。
- 内存占用较高:由于存储键值对和哈希表结构,内存消耗大于传统列表。
注意事项
-
键的类型转换规则:
- 字符串键内容为合法整数时,会被转为整数(如
"123"
→123
)。 - 浮点数键会被截断为整数(如
3.9
→3
)。 null
键会被转为空字符串""
。
- 字符串键内容为合法整数时,会被转为整数(如
-
稀疏数组:允许非连续整数键。
$arr = [0 => "a", 2 => "b"]; // 键 1 不存在
-
list()
解构:快速提取索引数组的值。$data = ["Alice", 30, "[email protected]"]; list($name, $age, $email) = $data;
二,数组的类型
PHP 数组可以分为以下几种类型:
- 索引数组(Indexed Array) :数组的索引是数字,默认从
0
开始。可以手动或自动分配数字索引。 - 关联数组(Associative Array):数组的索引是字符串。可以为每个元素指定一个唯一的键。
- 多维数组(Multidimensional Array):数组中包含另一个数组,可以有任意层次。
1. 索引数组
索引数组是最常见的数组类型,元素通过数字索引访问。索引从 0 开始,也可以自定义从其他数字开始。
示例:创建一个简单的索引数组
php
$fruits = ["Apple", "Banana", "Cherry"];
echo $fruits[0]; // 输出 "Apple"
echo $fruits[1]; // 输出 "Banana"
2. 关联数组
关联数组使用字符串作为键,您可以为每个元素指定一个自定义的键。
示例:创建一个关联数组
php
$person = [
"name" => "John",
"age" => 25,
"city" => "New York"
];
echo $person["name"]; // 输出 "John"
echo $person["age"]; // 输出 25
3. 多维数组
多维数组是包含其他数组的数组。最常见的是二维数组,但也可以有三维或更多维度的数组。
示例:创建一个二维数组
php
$contacts = [
["name" => "John", "phone" => "1234567890"],
["name" => "Jane", "phone" => "0987654321"]
];
echo $contacts[0]["name"]; // 输出 "John"
echo $contacts[1]["phone"]; // 输出 "0987654321"
三,定义数组
1. 定义索引数组
索引数组(Indexed Array)是最常见的数组类型,它的元素通过数字索引来访问。默认情况下,索引从 0 开始。如果没有显式指定索引,PHP 会自动为数组元素分配索引。
示例:定义一个索引数组
php
$fruits = ["Apple", "Banana", "Cherry"];
上述代码定义了一个包含三个元素的数组,元素分别是 "Apple"
、"Banana"
和 "Cherry"
,它们的索引依次为 0
、1
、2
。
你也可以显式地指定索引:
示例:显式指定索引
php
$fruits = [0 => "Apple", 1 => "Banana", 2 => "Cherry"];
如果索引没有显式定义,PHP 会自动从 0 开始递增。
2. 定义关联数组
关联数组(Associative Array)是用键(key)来访问数组元素,而不是数字索引。键可以是字符串,也可以是数字。
示例:定义一个关联数组
php
$person = [
"name" => "John",
"age" => 25,
"city" => "New York"
];
在上述代码中,"name"
、"age"
和 "city"
是数组的键,它们对应的值分别是 "John"
、25
和 "New York"
。你可以通过这些键来访问对应的值。
3. 定义多维数组
多维数组是数组中的元素本身也是数组。最常见的多维数组是二维数组,但也可以有三维或更多维度的数组。
示例:定义一个二维数组
php
$contacts = [
["name" => "John", "phone" => "1234567890"],
["name" => "Jane", "phone" => "0987654321"]
];
在这个例子中,$contacts
是一个二维数组,它包含两个元素,每个元素本身是一个关联数组,存储了 "name"
和 "phone"
键对应的值。
示例:定义一个三维数组
php
$library = [
"fiction" => [
["title" => "Book 1", "author" => "Author 1"],
["title" => "Book 2", "author" => "Author 2"]
],
"non_fiction" => [
["title" => "Book 3", "author" => "Author 3"],
["title" => "Book 4", "author" => "Author 4"]
]
];
在这个例子中,$library
是一个三维数组,其中包含了两组分类(fiction
和 non_fiction
),每组包含多个图书的详细信息。
4. 定义空数组
你可以通过 []
或 array()
来定义一个空数组。
示例:定义空的索引数组
php
$emptyArray = [];
示例:定义空的关联数组
php
$emptyAssocArray = array();
5. 使用 array()
创建数组
在 PHP 中,你可以使用 array()
函数来显式地创建数组(在较旧的 PHP 版本中常用,但在 PHP 5.4 及以上版本中可以直接使用短数组语法 []
)。
示例:使用 array()
创建索引数组
php
$fruits = array("Apple", "Banana", "Cherry");
示例:使用 array()
创建关联数组
php
$person = array("name" => "John", "age" => 25, "city" => "New York");
6. 数组中的混合类型
PHP 的数组支持存储不同类型的元素,包括整数、字符串、布尔值、对象甚至其他数组。
示例:存储混合类型的数据
php
$info = [
"name" => "John",
"age" => 25,
"isStudent" => true,
"friends" => ["Mike", "Anna"]
];
在这个例子中,$info
是一个关联数组,其中 "name"
和 "age"
存储的是字符串和整数,"isStudent"
存储的是布尔值,而 "friends"
存储的是一个数组。
7. 数组元素的索引和键
- 索引数组的索引是从 0 开始的整数,或者你可以手动指定索引。
- 关联数组中的键可以是字符串或者整数。键通常是自定义的,必须是唯一的。
示例:手动指定索引
php
$fruits = [1 => "Apple", 3 => "Banana", 5 => "Cherry"];
// 数组索引为 1, 3, 5,元素分别为 "Apple", "Banana", "Cherry"
示例:使用字符串作为键
php
$person = ["first_name" => "John", "last_name" => "Doe"];
// 键为 "first_name" 和 "last_name",分别对应 "John" 和 "Doe"
8. 定义数组时的注意事项
- PHP 数组的键是 唯一的,如果在关联数组中使用相同的键,后定义的键会覆盖前面的键。
- 数组中的键是 区分大小写 的,所以
"name"
和"Name"
被视为不同的键。
四,访问数组
1. 通过索引访问索引数组
索引数组中的元素通过数字索引来访问。默认情况下,索引从 0 开始,依次递增。
示例:访问索引数组元素
php
$fruits = ["Apple", "Banana", "Cherry"];
echo $fruits[0]; // 输出 "Apple"
echo $fruits[1]; // 输出 "Banana"
echo $fruits[2]; // 输出 "Cherry"
2. 通过键访问关联数组
关联数组中的元素通过键来访问。键可以是字符串或数字。
示例:访问关联数组元素
php
$person = [
"name" => "John",
"age" => 25,
"city" => "New York"
];
echo $person["name"]; // 输出 "John"
echo $person["age"]; // 输出 25
echo $person["city"]; // 输出 "New York"
3. 通过循环遍历数组
有时你需要访问数组中的所有元素,最常用的方法是使用 foreach
循环。foreach
适用于索引数组和关联数组。
示例:遍历索引数组
php
$fruits = ["Apple", "Banana", "Cherry"];
foreach ($fruits as $fruit) {
echo $fruit . "\n"; // 输出每个水果的名称
}
示例:遍历关联数组
php
$person = [
"name" => "John",
"age" => 25,
"city" => "New York"
];
foreach ($person as $key => $value) {
echo "$key: $value\n"; // 输出每个键值对
}
4. 通过 key()
和 current()
函数访问数组元素
在 PHP 中,key()
和 current()
函数用于获取数组的当前键和值。key()
返回当前元素的键,current()
返回当前元素的值。
示例:使用 key()
和 current()
访问元素
php
$fruits = ["Apple", "Banana", "Cherry"];
echo key($fruits); // 输出 0
echo current($fruits); // 输出 "Apple"
这些函数用于遍历数组时,配合 next()
, prev()
, reset()
等函数,可以实现数组指针的移动。
5. 使用 isset()
检查数组元素是否存在
在访问数组元素之前,最好先检查元素是否存在。isset()
可以用来检查数组的键是否已经定义,并且其值是否为 null
。
示例:检查数组元素是否存在
php
$person = [
"name" => "John",
"age" => 25
];
if (isset($person["city"])) {
echo $person["city"];
} else {
echo "City key does not exist.";
}
isset()
会返回 true
如果元素存在且其值不是 null
,否则返回 false
。
6. 使用 array_key_exists()
检查键是否存在
与 isset()
类似,array_key_exists()
可以检查数组中是否存在特定的键。不同的是,array_key_exists()
会检查键是否存在,而不关心值是否为 null
。
示例:检查数组键是否存在
php
$person = [
"name" => "John",
"age" => 25,
"city" => null
];
if (array_key_exists("city", $person)) {
echo "City exists.\n"; // 输出 "City exists."
} else {
echo "City does not exist.\n";
}
7. 通过 array_search()
查找值的键
array_search()
函数可以用来查找一个值在数组中的键(索引)。如果值存在,它会返回对应的键,否则返回 false
。
示例:查找值的键
php
$fruits = ["Apple", "Banana", "Cherry"];
$key = array_search("Banana", $fruits);
if ($key !== false) {
echo "Banana is found at index $key."; // 输出 "Banana is found at index 1."
} else {
echo "Banana not found.";
}
8. 通过 in_array()
查找值是否存在
in_array()
函数用于检查某个值是否存在于数组中。如果值存在,它返回 true
,否则返回 false
。
示例:查找数组中是否存在某个值
php
$fruits = ["Apple", "Banana", "Cherry"];
if (in_array("Banana", $fruits)) {
echo "Banana is in the array.";
} else {
echo "Banana is not in the array.";
}
9. 通过 list()
解构数组
在处理一个包含多个值的数组时,list()
函数可以让你一次性解构数组元素并将它们赋值给变量。
示例:使用 list()
解构数组
php
$fruits = ["Apple", "Banana", "Cherry"];
list($first, $second, $third) = $fruits;
echo $first; // 输出 "Apple"
echo $second; // 输出 "Banana"
echo $third; // 输出 "Cherry"
10. 通过 array_walk()
遍历数组并修改元素
array_walk()
函数用于遍历数组并应用回调函数到数组的每个元素。它允许你在遍历数组的过程中对元素进行修改。
示例:使用 array_walk()
修改数组元素
php
$fruits = ["Apple", "Banana", "Cherry"];
array_walk($fruits, function (&$item) {
$item = strtoupper($item); // 将每个元素转换为大写
});
print_r($fruits); // 输出 ["APPLE", "BANANA", "CHERRY"]
11. 访问多维数组元素
对于多维数组,访问元素的方法和访问一维数组相似,只不过你需要使用多个索引或键。
示例:访问二维数组元素
php
$contacts = [
["name" => "John", "phone" => "1234567890"],
["name" => "Jane", "phone" => "0987654321"]
];
echo $contacts[0]["name"]; // 输出 "John"
echo $contacts[1]["phone"]; // 输出 "0987654321"
总结
在 PHP 中,访问数组元素有多种方式,主要包括:
- 索引数组 :通过数字索引访问(例如
$array[0]
)。 - 关联数组 :通过键访问(例如
$array["key"]
)。 - 遍历数组 :使用
foreach
循环来遍历数组的元素。 - 函数工具 :通过函数如
isset()
,array_key_exists()
,array_search()
,in_array()
等来判断数组的元素是否存在。 - 多维数组:通过多个索引或键访问数组中的元素。
五,删除数组
1. 删除指定索引的元素 (unset()
)
unset()
是 PHP 中用于删除数组元素的函数。你可以使用它删除数组中的某个元素(通过指定索引或键)。删除元素后,数组的索引会变成不连续的。
示例:删除索引数组中的元素
php
$fruits = ["Apple", "Banana", "Cherry"];
unset($fruits[1]); // 删除索引为 1 的元素 "Banana"
print_r($fruits); // 输出: Array ( [0] => Apple [2] => Cherry )
示例:删除关联数组中的元素
php
$person = [
"name" => "John",
"age" => 25,
"city" => "New York"
];
unset($person["age"]); // 删除键为 "age" 的元素
print_r($person); // 输出: Array ( [name] => John [city] => New York )
注意 :
unset()
删除元素后,数组的索引不会自动重新排序。如果你希望索引连续,可以使用array_values()
函数。
2. 删除数组中的最后一个元素 (array_pop()
)
array_pop()
用于删除数组中的最后一个元素,并返回该元素的值。它会直接改变原数组,删除数组末尾的元素。
示例:删除最后一个元素
php
$fruits = ["Apple", "Banana", "Cherry"];
$lastFruit = array_pop($fruits);
echo $lastFruit; // 输出 "Cherry"
print_r($fruits); // 输出: Array ( [0] => Apple [1] => Banana )
3. 删除数组中的第一个元素 (array_shift()
)
array_shift()
用于删除数组中的第一个元素,并返回该元素的值。它会直接改变原数组,删除数组开头的元素。
示例:删除第一个元素
php
$fruits = ["Apple", "Banana", "Cherry"];
$firstFruit = array_shift($fruits);
echo $firstFruit; // 输出 "Apple"
print_r($fruits); // 输出: Array ( [0] => Banana [1] => Cherry )
4. 删除数组中的指定元素 (array_splice()
)
array_splice()
可以从数组中删除一段元素,并可以替换为新的值。你可以指定删除的起始位置和删除的元素数量。
示例:删除从索引 1 开始的 2 个元素
php
$fruits = ["Apple", "Banana", "Cherry", "Date"];
array_splice($fruits, 1, 2); // 从索引 1 开始,删除 2 个元素
print_r($fruits); // 输出: Array ( [0] => Apple [3] => Date )
示例:删除并替换元素
php
$fruits = ["Apple", "Banana", "Cherry", "Date"];
array_splice($fruits, 1, 2, ["Orange", "Peach"]); // 删除从索引 1 开始的 2 个元素,并替换为新元素
print_r($fruits); // 输出: Array ( [0] => Apple [1] => Orange [2] => Peach [3] => Date )
5. 删除数组中的元素并重置索引 (array_values()
)
array_values()
会返回一个新的数组,包含原数组中的所有值,但会重新索引数组,使得数组的索引从 0
开始。
示例:使用 array_values()
重新索引数组
php
$fruits = ["Apple", "Banana", "Cherry"];
unset($fruits[1]); // 删除索引为 1 的元素
$fruits = array_values($fruits); // 重新索引数组
print_r($fruits); // 输出: Array ( [0] => Apple [1] => Cherry )
6. 删除数组中的所有元素 (unset()
或 array_splice()
)
使用 unset()
删除整个数组
如果你想删除整个数组,可以直接使用 unset()
删除数组变量。
php
$fruits = ["Apple", "Banana", "Cherry"];
unset($fruits); // 删除整个数组
// 尝试访问 $fruits 会导致未定义错误
// echo $fruits; // 会报错
使用 array_splice()
删除所有元素
如果你只想删除数组中的所有元素而不删除数组本身,可以使用 array_splice()
。
php
$fruits = ["Apple", "Banana", "Cherry"];
array_splice($fruits, 0); // 从索引 0 开始,删除所有元素
print_r($fruits); // 输出: Array ( )
7. 删除数组中满足条件的元素
如果你需要删除数组中所有符合某个条件的元素,可以使用 array_filter()
。array_filter()
会返回一个新数组,包含所有符合条件的元素。你可以结合 array_values()
来重新索引数组。
示例:删除所有值为 "Banana" 的元素
php
$fruits = ["Apple", "Banana", "Cherry", "Banana"];
$fruits = array_filter($fruits, function($fruit) {
return $fruit !== "Banana";
});
$fruits = array_values($fruits); // 重新索引数组
print_r($fruits); // 输出: Array ( [0] => Apple [1] => Cherry )
8. 总结:常用的删除数组元素方法
方法 | 说明 | 示例 |
---|---|---|
unset() |
删除指定键或索引的数组元素 | unset($array[1]); |
array_pop() |
删除并返回数组的最后一个元素 | $last = array_pop($array); |
array_shift() |
删除并返回数组的第一个元素 | $first = array_shift($array); |
array_splice() |
删除数组中一部分元素,可以替换为新值 | array_splice($array, 1, 2, ["X", "Y"]); |
array_values() |
重新索引数组,使索引从 0 开始 | $array = array_values($array); |
array_filter() |
根据条件删除数组元素 | $array = array_filter($array, $callback); |
六,数组运算符
PHP 数组运算符列表
运算符 | 名称 | 描述 |
---|---|---|
+ |
联合(Union) | 合并两个数组,保留左侧数组的键,右侧重复的键会被忽略。 |
== |
松散相等 | 检查两个数组是否包含相同的键值对(顺序和类型不敏感)。 |
=== |
严格相等 | 检查两个数组是否包含相同的键值对,且顺序、类型完全一致。 |
!= |
松散不等 | 检查两个数组是否不相等(松散比较)。 |
<> |
松散不等(同 != ) |
与 != 功能相同。 |
!== |
严格不等 | 检查两个数组是否不完全相同(严格比较)。 |
示例代码
php
$a = array("a" => "apple", "b" => "banana");
$b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
// 合并运算符
$c = $a + $b;
var_dump($c);
// 相等运算符
var_dump($a == $b);
// 全等运算符
var_dump($a === $b);
结果解释
- 合并运算符 (
+
)- 结果:
array(3) { ["a"]=> string(5) "apple" ["b"]=> string(6) "banana" ["c"]=> string(6) "cherry" }
- 解释:
$a
和$b
合并后,$a
中的键值对保留,$b
中的键值对被附加到$a
后,但重复的键值不会被覆盖。
- 结果:
- 相等运算符 (
==
)- 结果:
bool(false)
- 解释:
$a
和$b
的键和值不完全相同,因此结果为false
。
- 结果:
- 全等运算符 (
===
)- 结果:
bool(false)
- 解释:
$a
和$b
的键和值虽然相同,但顺序不同,因此结果为false
。
- 结果:
详细说明与示例
1. 联合运算符 +
- 行为:合并两个数组,但左侧数组的键具有优先级。若键名重复,右侧的值会被忽略。
- 注意:键可以是数字或字符串,合并时根据键名去重,而非顺序。
php
$a = ["a" => 1, "b" => 2];
$b = ["b" => 3, "c" => 4];
$result = $a + $b;
// 结果:["a" => 1, "b" => 2, "c" => 4]
// 键 "b" 的值来自左侧 $a,右侧 $b 的 "b" 被忽略
2. 松散相等 ==
-
行为:检查两个数组的键值对是否相同,忽略顺序和键值类型差异。
-
示例:
php$arr1 = [1, "2"]; $arr2 = ["1", 2]; var_dump($arr1 == $arr2); // true(1 == "1" 且 "2" == 2)
3. 严格相等 ===
-
行为:键值对的顺序、类型、键名类型必须完全一致。
-
示例:
php$arr1 = [1, 2]; $arr2 = [1, 2]; $arr3 = [0 => 1, 1 => 2]; $arr4 = [1 => 2, 0 => 1]; var_dump($arr1 === $arr2); // true var_dump($arr1 === $arr3); // true(键顺序一致) var_dump($arr1 === $arr4); // false(键顺序不同)
4. 不等运算符 !=
/<>
和 !==
-
松散不等:检查数组是否不相等(松散比较)。
-
严格不等:检查数组是否不完全相同。
php$a = [1, 2]; $b = ["1", 2]; var_dump($a != $b); // false(松散相等) var_dump($a !== $b); // true(类型不同)
注意事项
-
+
运算符的特殊性:-
对数字键的数组,
+
可能导致意外结果:php$a = [1, 2]; $b = [3, 4]; $result = $a + $b; // 结果:[1, 2](右侧的 3、4 被忽略,因为键 0 和 1 已存在)
-
p如果需要合并数组并重置键名,使用
array_merge()
:php$result = array_merge($a, $b); // [1, 2, 3, 4]
-
-
严格相等 (
===
) 的严格性:-
键的顺序必须一致:
php$a = ["a" => 1, "b" => 2]; $b = ["b" => 2, "a" => 1]; var_dump($a === $b); // false(键顺序不同)
-
-
键名类型的敏感性:
php$a = [1 => "apple"]; $b = ["1" => "apple"]; var_dump($a == $b); // true(松散比较,键名类型不敏感) var_dump($a === $b); // false(严格比较,键名类型不同)
数组运算符应用场景
- 合并数组 :若需保留左侧数组的键,使用
+
;若需覆盖重复键或重置数字键,用array_merge()
。 - 快速比较数组内容 :使用
==
或===
判断键值对是否匹配。 - 精确匹配 :需要完全一致时(如缓存键的生成),必须用
===
。
七,多维数组
1. 创建多维数组
二维数组(矩阵)
php
// 使用短语法 [] 创建二维数组
$matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 关联式二维数组(如数据库查询结果)
$users = [
["id" => 1, "name" => "Alice", "age" => 30],
["id" => 2, "name" => "Bob", "age" => 25],
["id" => 3, "name" => "Charlie", "age" => 35]
];
三维数组
php
$cube = [
[
[1, 2],
[3, 4]
],
[
[5, 6],
[7, 8]
]
];
2. 访问多维数组元素
通过索引或键名
php
// 访问二维数组元素
echo $matrix[0][1]; // 输出:2
echo $users[1]["name"]; // 输出:Bob
// 访问三维数组元素
echo $cube[0][1][0]; // 输出:3
避免未定义键错误
使用 isset()
检查是否存在:
php
if (isset($users[2]["age"])) {
echo $users[2]["age"]; // 输出:35
}
3. 遍历多维数组
嵌套循环遍历
php
foreach ($users as $user) {
echo "User ID: " . $user["id"] . "\n";
foreach ($user as $key => $value) {
echo "$key: $value\n";
}
echo "--------\n";
}
递归遍历(未知层级深度)
php
function printNestedArray($array) {
foreach ($array as $key => $value) {
if (is_array($value)) {
printNestedArray($value);
} else {
echo "$key: $value\n";
}
}
}
printNestedArray($cube);
4. 修改多维数组
添加/更新元素
php
// 添加新用户
$users[] = ["id" => 4, "name" => "Dave", "age" => 28];
// 修改特定值
$users[0]["age"] = 31; // Alice 的年龄更新为 31
删除元素
php
unset($matrix[1][2]); // 删除第二行的第三个元素
5. 常用操作函数
提取列数据(array_column
)
php
$names = array_column($users, "name");
// 结果:["Alice", "Bob", "Charlie"]
递归合并(array_merge_recursive
)
php
$config1 = ["database" => ["host" => "localhost"]];
$config2 = ["database" => ["port" => 3306]];
$merged = array_merge_recursive($config1, $config2);
/*
结果:
[
"database" => [
"host" => "localhost",
"port" => 3306
]
]
*/
递归处理(array_walk_recursive
)
php
array_walk_recursive($cube, function (&$value) {
$value *= 2; // 将所有数值翻倍
});
6. 应用场景
- 表格数据:数据库查询结果集通常以二维数组形式返回。
- 配置信息:嵌套配置(如多环境参数)。
- JSON 解析:处理复杂 JSON 结构(如 API 响应)。
- 树形结构:表示目录、分类层级等。
注意事项
-
层级深度:避免过深嵌套,否则代码可读性和性能会下降。
-
内存占用:多维数组可能占用较多内存,处理大数据时需优化。
-
键名冲突:合并多维数组时注意键名覆盖问题。
-
引用陷阱 :使用引用操作符
&
时,确保理解其副作用。phpforeach ($users as &$user) { $user["age"] += 1; // 正确:直接修改原数组 } unset($user); // 必须取消引用,避免后续误操作
示例:处理 JSON 数据
php
$json = '{
"users": [
{"name": "Alice", "scores": [80, 90, 85]},
{"name": "Bob", "scores": [70, 88, 92]}
]
}';
$data = json_decode($json, true); // 转为关联数组
// 计算每个用户的总分
foreach ($data["users"] as &$user) {
$user["total"] = array_sum($user["scores"]);
}
unset($user);
print_r($data);
八,数组的遍历
1. foreach
循环(最常用)
语法形式
-
遍历值:
phpforeach ($array as $value) { // 操作 $value }
-
遍历键和值:
phpforeach ($array as $key => $value) { // 操作 $key 和 $value }
示例
php
$fruits = ["apple", "banana", "cherry"];
// 遍历值
foreach ($fruits as $fruit) {
echo $fruit . "\n";
}
// 输出:apple banana cherry
// 遍历键和值
$user = ["name" => "Alice", "age" => 30];
foreach ($user as $key => $value) {
echo "$key: $value\n";
}
// 输出:name: Alice age: 30
特点
-
自动处理指针:无需手动重置数组指针。
-
引用修改 :使用
&
可直接修改原数组元素。php$numbers = [1, 2, 3]; foreach ($numbers as &$num) { $num *= 2; // 直接修改原数组 } unset($num); // 必须取消引用,避免后续误操作 print_r($numbers); // [2, 4, 6]
一、PHP 数组的底层结构
PHP 数组(zend_array
)本质是一个有序的哈希表(Ordered HashTable),结合了哈希表和链表的特性:
- 哈希表:快速通过键(整数或字符串)查找值(时间复杂度接近 O(1))。
- 双向链表:维护插入顺序,保证遍历顺序与元素插入顺序一致(PHP 7+ 严格保证)。
每个数组元素存储为一个 Bucket
结构,包含:
- 键(
key
,整数或字符串) - 值(
value
,zval
结构,存储实际数据) - 链表指针(
next
和prev
,用于维护插入顺序)
二、foreach
的底层流程
当执行 foreach
时,PHP 引擎会进行以下操作:
1. 初始化遍历
- 创建内部指针副本 :
foreach
遍历时,会复制原数组的内部指针状态 ,而非直接操作原数组的指针。这意味着:- 遍历不会影响原数组的指针位置(如
current()
、next()
的结果)。 - 多次遍历同一数组时,每次
foreach
都会独立进行。
- 遍历不会影响原数组的指针位置(如
2. 遍历过程
- 顺序遍历链表 :按插入顺序依次访问每个
Bucket
(通过双向链表的next
指针)。 - 返回键值对 :对于每个元素,返回键(
$key
)和值($value
)。
3. 写时复制(Copy-On-Write, COW)
- 值传递时的优化 :默认情况下,PHP 使用写时复制 机制。遍历时:
- 如果未通过引用(
&
)修改元素值,foreach
操作的是原数组的副本,原数组不会被修改。 - 如果通过引用修改元素值,直接操作原数组。
- 如果未通过引用(
4. 引用遍历的特殊处理
当使用 foreach ($array as &$value)
时:
-
直接绑定到原数组元素 :
$value
是原数组元素的引用。 -
潜在陷阱 :循环结束后需手动
unset($value)
,否则后续操作可能意外修改原数组。php$arr = [1, 2, 3]; foreach ($arr as &$v) { /* ... */ } unset($v); // 必须取消引用
三、关键行为解析
1. 遍历过程中修改数组
-
添加/删除元素:可能导致遍历行为不可预测(如跳过元素或重复遍历)。
php$arr = [1, 2, 3]; foreach ($arr as $v) { echo $v; // 输出:1 2 3 unset($arr[1]); // 删除元素 2,但遍历继续 }
-
修改元素值:
- 非引用模式:修改的是副本,原数组不变。
- 引用模式:直接修改原数组。
2. 性能优化
- 直接操作内部结构 :
foreach
直接遍历哈希表的链表结构,无需计算键范围(比for
循环更高效)。 - 无重复计算 :
foreach
预先获取数组长度,避免多次调用count()
。
3. 多维数组遍历
-
嵌套
foreach
:每次遍历子数组时,会为子数组创建独立的指针副本。php$matrix = [[1, 2], [3, 4]]; foreach ($matrix as $row) { foreach ($row as $num) { echo $num; // 1 2 3 4 } }
四、底层代码模拟
以下是 foreach
的伪代码逻辑(简化版):
c
// 伪代码:模拟 PHP 的 foreach 行为
zend_array *arr = ...; // 原数组
zend_array *copy = NULL;
// 1. 复制数组指针状态(写时复制)
if (需要写时复制) {
copy = zend_array_dup(arr); // 创建副本
} else {
copy = arr;
}
// 2. 遍历链表
Bucket *current = copy->head; // 链表头部
while (current != NULL) {
zval *key = current->key;
zval *value = ¤t->val;
// 3. 传递键值给用户代码
PHP_USER_CODE(key, value);
current = current->next; // 下一个元素
}
五、注意事项
-
引用遍历后的清理:
php$arr = [1, 2, 3]; foreach ($arr as &$v) { /* ... */ } unset($v); // 必须取消引用,否则后续操作可能修改 $arr[2]
-
避免遍历中修改数组结构:
php// 危险操作! foreach ($arr as $key => $value) { unset($arr[$key + 1]); // 可能破坏链表结构 }
-
多维数组的引用陷阱:
php$data = [[1, 2], [3, 4]]; foreach ($data as &$row) { $row[0] *= 2; // 直接修改原数组 } unset($row); // $data 变为 [[2, 2], [6, 4]]
foreach
的高效性源于 PHP 数组的哈希表结构和写时复制机制,其核心行为包括:
- 独立指针副本:遍历不影响原数组指针。
- 引用直接操作原数据:需谨慎处理生命周期。
- 顺序遍历链表:保证插入顺序一致性。
2. for
循环(仅适用于索引数组)
语法形式
php
for ($i = 0; $i < count($array); $i++) {
// 操作 $array[$i]
}
示例
php
$colors = ["red", "green", "blue"];
for ($i = 0; $i < count($colors); $i++) {
echo $colors[$i] . "\n";
}
// 输出:red green blue
适用场景
- 数组键为连续整数(索引数组)。
- 需要精确控制遍历顺序或索引。
缺点
-
无法直接遍历关联数组。
-
性能略低(每次循环都调用
count()
,建议提前缓存长度):php$len = count($colors); for ($i = 0; $i < $len; $i++) { ... }
3. while
+ each()
+ list()
(PHP 7.2 后废弃)
语法形式
php
reset($array); // 重置指针到起始位置
while (list($key, $value) = each($array)) {
// 操作 $key 和 $value
}
示例
php
$animals = ["dog", "cat", "bird"];
reset($animals);
while (list($key, $value) = each($animals)) {
echo "$key: $value\n";
}
// 输出:0: dog 1: cat 2: bird
注意
- 已弃用 :PHP 7.2 后
each()
函数被废弃,建议改用foreach
。
4. 数组指针函数(current()
, next()
, reset()
等)
语法形式
php
reset($array); // 重置指针到第一个元素
while ($value = current($array)) {
// 操作 $value
next($array); // 移动指针到下一个元素
}
示例
php
$languages = ["PHP", "Python", "Java"];
reset($languages);
while ($value = current($languages)) {
echo $value . "\n";
next($languages);
}
// 输出:PHP Python Java
适用场景
- 需要手动控制指针位置(如跳过某些元素)。
- 遍历过程中可能中断并恢复遍历。
5. array_walk()
和 array_walk_recursive()
array_walk()
-
遍历并处理每个元素:
php$numbers = [1, 2, 3]; array_walk($numbers, function (&$value, $key) { $value *= 2; // 修改原数组 }); print_r($numbers); // [2, 4, 6]
array_walk_recursive()
-
递归遍历多维数组:
php$data = [ "a" => [1, 2], "b" => [3, 4] ]; array_walk_recursive($data, function ($value) { echo $value . "\n"; // 输出:1 2 3 4 });
6. array_map()
(映射处理)
-
对每个元素应用回调函数,返回新数组:
php$nums = [1, 2, 3]; $squared = array_map(function ($n) { return $n * $n; }, $nums); print_r($squared); // [1, 4, 9]
7. 多维数组遍历
嵌套 foreach
php
$students = [
["name" => "Alice", "scores" => [80, 90]],
["name" => "Bob", "scores" => [70, 85]]
];
foreach ($students as $student) {
echo $student["name"] . ": ";
foreach ($student["scores"] as $score) {
echo $score . " ";
}
echo "\n";
}
// 输出:
// Alice: 80 90
// Bob: 70 85
遍历时的注意事项
- 修改数组元素 :
foreach
中使用引用 (&$value
) 可直接修改原数组。- 其他方法需通过键名修改(如
$array[$key] = ...
)。
- 性能优化 :
- 优先使用
foreach
(PHP 内部优化,效率高)。 - 避免在循环内重复调用
count()
或sizeof()
。
- 优先使用
- 多维数组的深度遍历 :
- 使用递归或
array_walk_recursive()
。
- 使用递归或
总结
方法 | 适用场景 | 特点 |
---|---|---|
foreach |
任意数组(推荐默认选择) | 简洁高效,支持引用修改 |
for |
连续索引数组 | 需手动控制索引,性能略低 |
array_walk() |
批量处理元素 | 支持回调函数,可处理多维数组 |
array_map() |
生成新数组 | 不修改原数组,纯函数式操作 |
九,数组相关函数
一、数组创建与填充
1. array()
/ []
php
$arr = array(1, 2, 3); // 传统语法
$arr = [1, 2, 3]; // 短数组语法(PHP 5.4+)
2. range()
- 生成数值范围数组
php
$numbers = range(1, 5); // [1,2,3,4,5]
$letters = range('a', 'e'); // ['a','b','c','d','e']
3. array_fill()
- 填充数组
php
$arr = array_fill(0, 3, 'PHP'); // [0 => 'PHP', 1 => 'PHP', 2 => 'PHP']
4. array_combine()
- 合并键值数组
php
$keys = ['a', 'b', 'c'];
$values = [1, 2, 3];
$combined = array_combine($keys, $values); // ['a'=>1, 'b'=>2, 'c'=>3]
二、元素增删改查
1. 增删元素
函数 | 描述 | 示例 |
---|---|---|
array_push() |
末尾添加元素(可多个) | array_push($arr, 4, 5); |
array_pop() |
删除并返回最后一个元素 | $last = array_pop($arr); |
array_unshift() |
开头添加元素(可多个) | array_unshift($arr, 0); |
array_shift() |
删除并返回第一个元素 | $first = array_shift($arr); |
unset() |
删除指定键的元素(直接操作数组) | unset($arr[1]); |
2. 元素查找
函数 | 描述 | 示例 |
---|---|---|
in_array() |
检查值是否存在 | in_array(2, $arr) |
array_search() |
查找值的键名(返回第一个匹配) | $key = array_search(2, $arr); |
array_key_exists() |
检查键是否存在 | array_key_exists('name', $arr); |
isset() |
检查键是否存在且值非 null | isset($arr['name']); |
三、数组遍历与处理
1. 遍历处理
函数 | 描述 | 示例 |
---|---|---|
array_map() |
对每个元素应用回调函数 | array_map(fn($n) => $n*2, $arr); |
array_walk() |
遍历修改元素(可传引用) | array_walk($arr, function(&$v) { $v++; }); |
array_filter() |
过滤元素(保留回调返回 true 的) | array_filter($arr, fn($n) => $n > 2); |
2. 多维数组处理
php
// array_column - 提取多维数组的列
$users = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob']
];
$names = array_column($users, 'name'); // ['Alice', 'Bob']
// array_walk_recursive - 递归处理多维数组
$data = [[1, 2], [3, [4]]];
array_walk_recursive($data, function(&$v) { $v *= 2; }); // [[2,4], [6, [8]]]
四、数组合并与拆分
1. 合并数组
函数 | 行为差异 | 示例 |
---|---|---|
array_merge() |
覆盖重复键(数字键重新索引) | array_merge([1,2], [3,4]); → [1,2,3,4] |
+ 运算符 |
保留左侧键(忽略右侧重复键) | [1,2] + [3,4] → [1,2] |
array_merge_recursive() |
递归合并多维数组 | array_merge_recursive(['a'=>1], ['a'=>2]); → ['a' => [1,2]] |
2. 拆分数组
函数 | 描述 | 示例 |
---|---|---|
array_slice() |
截取子数组(不修改原数组) | array_slice($arr, 1, 2); |
array_splice() |
截取并替换原数组内容 | array_splice($arr, 1, 2, ['x','y']); |
explode() / implode() |
字符串与数组互转 | implode(',', [1,2,3]); → "1,2,3" |
五、数组排序
1. 常用排序函数
函数 | 排序依据 | 是否保持键关联 | 示例 |
---|---|---|---|
sort() |
值升序 | 否(重置键) | sort($arr); |
rsort() |
值降序 | 否 | rsort($arr); |
asort() |
值升序 | 是 | asort($arr); |
arsort() |
值降序 | 是 | arsort($arr); |
ksort() |
键升序 | 是 | ksort($arr); |
krsort() |
键降序 | 是 | krsort($arr); |
usort() |
自定义比较函数 | 否 | usort($arr, fn($a,$b) => $a <=> $b); |
2. 多维数组排序
php
$users = [
['name' => 'Bob', 'age' => 25],
['name' => 'Alice', 'age' => 30]
];
// 按 age 升序排序
usort($users, function($a, $b) {
return $a['age'] <=> $b['age'];
});
六、其他实用函数
函数 | 描述 | 示例 |
---|---|---|
array_unique() |
去重(保留第一个出现的值) | array_unique([1,2,2,3]); → [1,2,3] |
array_flip() |
交换键值(值需为合法键类型) | array_flip(['a'=>1, 'b'=>2]); → [1=>'a', 2=>'b'] |
array_reverse() |
反转数组顺序 | array_reverse([1,2,3]); → [3,2,1] |
count() / sizeof() |
统计元素数量 | count($arr); |
shuffle() |
随机打乱数组顺序 | shuffle($arr); |
七,数组指针函数
核心数组指针函数
-
reset(array &$array): mixed
-
将数组内部指针重置到第一个元素,并返回该元素的值。
-
示例:
php$arr = ['a', 'b', 'c']; echo reset($arr); // 输出 'a'
-
-
end(array &$array): mixed
-
将指针移动到数组最后一个元素,并返回其值。
-
示例:
phpecho end($arr); // 输出 'c'
-
-
current(array &$array): mixed
-
返回当前指针指向的元素值(不移动指针)。
-
如果指针越界,返回
false
。 -
示例:
phpreset($arr); echo current($arr); // 输出 'a'
-
-
next(array &$array): mixed
-
将指针向前移动一位 ,并返回新位置的值。若越界则返回
false
。 -
示例:
phpnext($arr); echo current($arr); // 输出 'b'
-
-
prev(array &$array): mixed
-
将指针向后移动一位 ,返回新位置的值。若越界返回
false
。 -
示例:
phpprev($arr); echo current($arr); // 输出 'a'(指针回到第一个元素)
-
-
key(array &$array): int|string|null
-
返回当前元素的键名。若指针越界,返回
null
。 -
示例:
phpreset($arr); echo key($arr); // 输出 0(索引数组的键)
-
注意事项
-
each()
函数已废弃在 PHP 7.2+ 中,
each()
函数被废弃,建议用foreach
替代。旧用法示例:php// 已废弃!不建议使用! while (list($key, $value) = each($arr)) { echo "$key => $value"; }
-
指针越界问题
当指针移动到数组外部时,
current()
返回false
,但需注意false
可能也是合法的元素值。可通过key()
是否为null
判断是否越界:phpif (key($arr) === null) { echo '指针越界'; }
-
修改数组的影响
直接修改数组(如添加/删除元素)可能导致指针重置,建议操作后使用
reset()
重新定位。
遍历数组的替代方案
虽然指针函数可用于手动遍历,但通常更推荐 foreach
循环(更简洁且不依赖内部指针状态):
php
foreach ($arr as $key => $value) {
echo "$key => $value";
}
实用示例:手动遍历数组
php
$arr = ['x' => 'apple', 'y' => 'banana', 'z' => 'cherry'];
reset($arr); // 确保指针在起点
while (key($arr) !== null) {
echo key($arr) . ': ' . current($arr) . PHP_EOL;
next($arr);
}
// 输出:
// x: apple
// y: banana
// z: cherry
总结
- 适用场景:需要精准控制指针位置时(如部分遍历、反向操作等)。
- 避免场景 :优先使用
foreach
进行完整遍历。 - 废弃函数 :不再使用
each()
。
八、注意事项
-
引用传递陷阱:
php$arr = [1, 2]; $ref = &$arr; array_push($ref, 3); // $arr 变为 [1,2,3]
-
性能优化:
- 避免在循环内多次调用
count()
,提前缓存结果。 - 优先使用
isset()
而非array_key_exists()
(更快)。
- 避免在循环内多次调用
-
排序函数副作用:
sort()
、rsort()
等会修改原数组并重置键名。- 使用
asort()
、ksort()
保持键关联。
-
多维数组合并:
array_merge_recursive()
可能产生嵌套结构,需注意层级。
总结
掌握 PHP 数组函数能极大提升开发效率,核心原则:
- 选择合适函数 :根据场景选择
array_map
(映射)、array_filter
(过滤)、array_reduce
(聚合)等。 - 注意引用与拷贝:明确函数是否修改原数组或返回新数组。
- 组合使用函数 :如
array_column
+array_filter
快速提取并过滤数据。
十,数组模拟栈的行为
在 PHP 中,栈(Stack) 是一种常见的数据结构,遵循 后进先出(LIFO) 原则。虽然 PHP 没有专门的 Stack
类,但可以通过数组和内置函数轻松实现栈的行为。以下是栈的核心操作及其在 PHP 中的实现方式:
栈的核心操作
操作 | 描述 | PHP 实现 |
---|---|---|
Push | 将元素压入栈顶 | array_push() 或 $array[] = $value |
Pop | 弹出栈顶元素并返回 | array_pop() |
Peek | 查看栈顶元素(不弹出) | end($array) |
Size | 获取栈中元素数量 | count($array) |
IsEmpty | 判断栈是否为空 | empty($array) |
PHP 实现栈的两种方式
1. 使用数组函数
php
$stack = [];
// 压栈(Push)
array_push($stack, "A", "B"); // 栈内: ["A", "B"]
$stack[] = "C"; // 更高效的方式,栈内: ["A", "B", "C"]
// 弹栈(Pop)
$top = array_pop($stack); // $top = "C",栈内: ["A", "B"]
// 查看栈顶(Peek)
$peek = end($stack); // $peek = "B"(指针移动到末尾)
// 重置指针(可选)
reset($stack);
// 判断是否为空
if (empty($stack)) {
echo "栈为空";
}
2. 手动实现栈逻辑
(适合理解底层原理)
php
class Stack {
private $items = [];
public function push($item) {
$this->items[] = $item; // 直接操作数组末尾
}
public function pop() {
if ($this->isEmpty()) {
return null;
}
return array_pop($this->items);
}
public function peek() {
return end($this->items);
}
public function isEmpty() {
return empty($this->items);
}
public function size() {
return count($this->items);
}
}
// 使用示例
$stack = new Stack();
$stack->push("X");
$stack->push("Y");
echo $stack->pop(); // 输出 "Y"
为什么推荐使用数组直接操作?
- 高效性 :
$array[] = $value
比array_push()
更快,因为它避免了函数调用开销。 - 简洁性 :PHP 数组天然支持栈的尾部操作(
array_pop
和array_push
)。 - 灵活性 :可以结合其他数组函数(如
array_slice
)实现更复杂的栈逻辑。
栈的经典应用场景
1. 括号匹配检查
php
function isBalanced($str) {
$stack = [];
$pairs = [')' => '(', ']' => '[', '}' => '{'];
for ($i = 0; $i < strlen($str); $i++) {
$char = $str[$i];
if (in_array($char, ['(', '[', '{'])) {
array_push($stack, $char);
} elseif (in_array($char, [')', ']', '}'])) {
if (empty($stack) || array_pop($stack) != $pairs[$char]) {
return false;
}
}
}
return empty($stack);
}
echo isBalanced("({[]})") ? "合法" : "非法"; // 输出 "非法"
2. 撤销操作(Undo)
php
$history = [];
$currentState = "";
// 记录状态变化
function saveState(&$history, &$currentState) {
array_push($history, $currentState);
}
// 撤销到上一个状态
function undo(&$history) {
if (!empty($history)) {
return array_pop($history);
}
return null;
}
// 示例
saveState($history, $currentState);
$currentState = "编辑1";
saveState($history, $currentState);
$currentState = "编辑2";
echo undo($history); // 返回 "编辑1"
注意事项
- 避免使用
array_shift()
/array_unshift()
这些函数操作数组开头,时间复杂度为 O(n),而栈应只操作尾部(O(1) 时间)。 - 指针函数与栈的关系
栈通常只关心顶部元素,end()
可查看栈顶,但无需依赖next()
或prev()
。 - 引用传递问题
直接操作数组时,注意函数参数是否需要引用(例如end($array)
会移动内部指针)。
总结
-
PHP 数组即栈 :通过
array_push()
/array_pop()
或$array[]
语法即可实现高效栈操作。 -
应用广泛:从算法问题(如括号匹配)到实际功能(如撤销记录)均可使用栈。
-
性能优先:操作数组尾部比操作头部高效,避免不必要的指针移动。
十一,数组模拟队列的行为
在 PHP 中,队列(Queue) 是一种遵循 先进先出(FIFO) 原则的数据结构。虽然 PHP 没有原生的 Queue
类,但可以通过数组和内置函数模拟队列行为。以下是队列的核心操作及其 PHP 实现方法,包括性能优化技巧和注意事项。
队列的核心操作
操作 | 描述 | PHP 实现(数组模拟) |
---|---|---|
Enqueue | 元素入队(添加到队尾) | array_push() 或 $array[] = $value |
Dequeue | 元素出队(移除并返回队头) | array_shift() |
Peek | 查看队头元素(不移除) | reset($array) |
Size | 获取队列元素数量 | count($array) |
IsEmpty | 判断队列是否为空 | empty($array) |
PHP 实现队列的两种方式
1. 直接使用数组函数
php
$queue = [];
// 入队(Enqueue)
array_push($queue, "A"); // 队尾添加元素,队列: ["A"]
$queue[] = "B"; // 更高效的写法,队列: ["A", "B"]
// 出队(Dequeue)
$front = array_shift($queue); // $front = "A",队列: ["B"]
// 查看队头(Peek)
$peek = reset($queue); // $peek = "B"(指针重置到头部)
// 判断是否为空
if (empty($queue)) {
echo "队列为空";
}
2. 手动优化性能(避免重新索引)
PHP 的 array_shift()
在移除数组第一个元素时会导致所有剩余元素的键被重新索引(时间复杂度 O(n) )。对于高频操作的大队列,可以通过 维护头尾指针 或使用 关联数组 来优化性能:
php
class OptimizedQueue {
private $items = [];
private $front = 0; // 队头索引
private $rear = -1; // 队尾索引
public function enqueue($item) {
$this->items[++$this->rear] = $item; // 队尾插入
}
public function dequeue() {
if ($this->isEmpty()) {
return null;
}
$item = $this->items[$this->front];
unset($this->items[$this->front]);
$this->front++; // 仅移动指针,不重新索引
return $item;
}
public function peek() {
return $this->items[$this->front] ?? null;
}
public function isEmpty() {
return $this->front > $this->rear;
}
public function size() {
return $this->rear - $this->front + 1;
}
}
// 使用示例
$queue = new OptimizedQueue();
$queue->enqueue("X");
$queue->enqueue("Y");
echo $queue->dequeue(); // 输出 "X"
队列的经典应用场景
1. 任务调度(先进先处理)
php
$taskQueue = [];
array_push($taskQueue, "发送邮件");
array_push($taskQueue, "生成报表");
while (!empty($taskQueue)) {
$task = array_shift($taskQueue);
echo "处理任务: $task\n";
}
// 输出:
// 处理任务: 发送邮件
// 处理任务: 生成报表
2. 广度优先搜索(BFS)
php
function bfs($graph, $startNode) {
$visited = [];
$queue = [];
array_push($queue, $startNode);
$visited[$startNode] = true;
while (!empty($queue)) {
$node = array_shift($queue);
echo "访问节点: $node\n";
foreach ($graph[$node] as $neighbor) {
if (!isset($visited[$neighbor])) {
$visited[$neighbor] = true;
array_push($queue, $neighbor);
}
}
}
}
$graph = [
'A' => ['B', 'C'],
'B' => ['D'],
'C' => ['E'],
'D' => [],
'E' => []
];
bfs($graph, 'A');
注意事项
-
避免频繁使用
array_shift()
直接操作数组头部会导致所有元素重新索引,时间复杂度为 O(n) 。若队列规模较大,建议使用优化的手动实现(如
OptimizedQueue
类)或 PHP 的SplQueue
。 -
使用
SplQueue
扩展(高效替代方案)PHP 的
SplQueue
基于双向链表实现,入队/出队均为 O(1) 时间复杂度:php$queue = new SplQueue(); $queue->enqueue("A"); // 入队 $queue->enqueue("B"); echo $queue->dequeue(); // 输出 "A"
-
关联数组优化
通过维护头尾指针(如
OptimizedQueue
类),可以避免数组重新索引,但需要手动管理逻辑。 -
键名溢出问题
手动优化实现中,若队列长期运行且
rear
超出 PHP 的整数范围,需重置键名(实际场景中罕见)。
总结
-
简单场景 :直接使用
array_push()
+array_shift()
,适用于小规模队列。 -
高频/大数据量 :推荐手动优化实现或
SplQueue
。 -
替代方案 :使用 Redis 或消息队列服务(如 RabbitMQ)处理分布式或持久化需求。
nt]);
$this->front++; // 仅移动指针,不重新索引
return $item;
}
public function peek() {
return t h i s − > i t e m s [ this->items[ this−>items[this->front] ?? null;
}
public function isEmpty() {
return this-\>front \> this->rear;
}
public function size() {
return this-\>rear - this->front + 1;
}
}
// 使用示例
$queue = new OptimizedQueue();
$queue->enqueue("X");
$queue->enqueue("Y");
echo $queue->dequeue(); // 输出 "X"
------
### **队列的经典应用场景**
#### **1. 任务调度(先进先处理)**
```php
$taskQueue = [];
array_push($taskQueue, "发送邮件");
array_push($taskQueue, "生成报表");
while (!empty($taskQueue)) {
$task = array_shift($taskQueue);
echo "处理任务: $task\n";
}
// 输出:
// 处理任务: 发送邮件
// 处理任务: 生成报表
2. 广度优先搜索(BFS)
php
function bfs($graph, $startNode) {
$visited = [];
$queue = [];
array_push($queue, $startNode);
$visited[$startNode] = true;
while (!empty($queue)) {
$node = array_shift($queue);
echo "访问节点: $node\n";
foreach ($graph[$node] as $neighbor) {
if (!isset($visited[$neighbor])) {
$visited[$neighbor] = true;
array_push($queue, $neighbor);
}
}
}
}
$graph = [
'A' => ['B', 'C'],
'B' => ['D'],
'C' => ['E'],
'D' => [],
'E' => []
];
bfs($graph, 'A');
注意事项
-
避免频繁使用
array_shift()
直接操作数组头部会导致所有元素重新索引,时间复杂度为 O(n) 。若队列规模较大,建议使用优化的手动实现(如
OptimizedQueue
类)或 PHP 的SplQueue
。 -
使用
SplQueue
扩展(高效替代方案)PHP 的
SplQueue
基于双向链表实现,入队/出队均为 O(1) 时间复杂度:php$queue = new SplQueue(); $queue->enqueue("A"); // 入队 $queue->enqueue("B"); echo $queue->dequeue(); // 输出 "A"
-
关联数组优化
通过维护头尾指针(如
OptimizedQueue
类),可以避免数组重新索引,但需要手动管理逻辑。 -
键名溢出问题
手动优化实现中,若队列长期运行且
rear
超出 PHP 的整数范围,需重置键名(实际场景中罕见)。
总结
- 简单场景 :直接使用
array_push()
+array_shift()
,适用于小规模队列。 - 高频/大数据量 :推荐手动优化实现或
SplQueue
。 - 替代方案:使用 Redis 或消息队列服务(如 RabbitMQ)处理分布式或持久化需求。