第八章-PHP数组

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)。
  • 内存占用较高:由于存储键值对和哈希表结构,内存消耗大于传统列表。

注意事项

  1. 键的类型转换规则

    • 字符串键内容为合法整数时,会被转为整数(如 "123"123)。
    • 浮点数键会被截断为整数(如 3.93)。
    • null 键会被转为空字符串 ""
  2. 稀疏数组:允许非连续整数键。

    复制代码
    $arr = [0 => "a", 2 => "b"]; // 键 1 不存在
  3. list() 解构:快速提取索引数组的值。

    复制代码
    $data = ["Alice", 30, "alice@example.com"];
    list($name, $age, $email) = $data;

二,数组的类型

PHP 数组可以分为以下几种类型:

  1. 索引数组(Indexed Array) :数组的索引是数字,默认从 0 开始。可以手动或自动分配数字索引。
  2. 关联数组(Associative Array):数组的索引是字符串。可以为每个元素指定一个唯一的键。
  3. 多维数组(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",它们的索引依次为 012

你也可以显式地指定索引:

示例:显式指定索引
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 是一个三维数组,其中包含了两组分类(fictionnon_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);

结果解释

  1. 合并运算符 (+)
    • 结果: array(3) { ["a"]=> string(5) "apple" ["b"]=> string(6) "banana" ["c"]=> string(6) "cherry" }
    • 解释: $a$b 合并后,$a 中的键值对保留,$b 中的键值对被附加到 $a 后,但重复的键值不会被覆盖。
  2. 相等运算符 (==)
    • 结果: bool(false)
    • 解释: $a$b 的键和值不完全相同,因此结果为 false
  3. 全等运算符 (===)
    • 结果: 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(类型不同)

注意事项

  1. + 运算符的特殊性

    • 对数字键的数组,+ 可能导致意外结果:

      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]
  2. 严格相等 (===) 的严格性

    • 键的顺序必须一致:

      php 复制代码
      $a = ["a" => 1, "b" => 2];
      $b = ["b" => 2, "a" => 1];
      var_dump($a === $b); // false(键顺序不同)
  3. 键名类型的敏感性

    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. 应用场景

  1. 表格数据:数据库查询结果集通常以二维数组形式返回。
  2. 配置信息:嵌套配置(如多环境参数)。
  3. JSON 解析:处理复杂 JSON 结构(如 API 响应)。
  4. 树形结构:表示目录、分类层级等。

注意事项

  1. 层级深度:避免过深嵌套,否则代码可读性和性能会下降。

  2. 内存占用:多维数组可能占用较多内存,处理大数据时需优化。

  3. 键名冲突:合并多维数组时注意键名覆盖问题。

  4. 引用陷阱 :使用引用操作符 & 时,确保理解其副作用。

    php 复制代码
    foreach ($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 循环(最常用)

语法形式
  • 遍历值

    php 复制代码
    foreach ($array as $value) {
        // 操作 $value
    }
  • 遍历键和值

    php 复制代码
    foreach ($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,整数或字符串)
  • 值(valuezval 结构,存储实际数据)
  • 链表指针(nextprev,用于维护插入顺序)

二、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 = &current->val;

    // 3. 传递键值给用户代码
    PHP_USER_CODE(key, value);

    current = current->next; // 下一个元素
}

五、注意事项
  1. 引用遍历后的清理

    php 复制代码
    $arr = [1, 2, 3];
    foreach ($arr as &$v) { /* ... */ }
    unset($v); // 必须取消引用,否则后续操作可能修改 $arr[2]
  2. 避免遍历中修改数组结构

    php 复制代码
    // 危险操作!
    foreach ($arr as $key => $value) {
        unset($arr[$key + 1]); // 可能破坏链表结构
    }
  3. 多维数组的引用陷阱

    php 复制代码
    $data = [[1, 2], [3, 4]];
    foreach ($data as &$row) {
        $row[0] *= 2; // 直接修改原数组
    }
    unset($row);
    // $data 变为 [[2, 2], [6, 4]]

foreach 的高效性源于 PHP 数组的哈希表结构和写时复制机制,其核心行为包括:

  1. 独立指针副本:遍历不影响原数组指针。
  2. 引用直接操作原数据:需谨慎处理生命周期。
  3. 顺序遍历链表:保证插入顺序一致性。

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

遍历时的注意事项

  1. 修改数组元素
    • foreach 中使用引用 (&$value) 可直接修改原数组。
    • 其他方法需通过键名修改(如 $array[$key] = ...)。
  2. 性能优化
    • 优先使用 foreach(PHP 内部优化,效率高)。
    • 避免在循环内重复调用 count()sizeof()
  3. 多维数组的深度遍历
    • 使用递归或 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);

七,数组指针函数

核心数组指针函数
  1. reset(array &$array): mixed

    • 将数组内部指针重置到第一个元素,并返回该元素的值。

    • 示例:

      php 复制代码
      $arr = ['a', 'b', 'c'];
      echo reset($arr); // 输出 'a'
  2. end(array &$array): mixed

    • 将指针移动到数组最后一个元素,并返回其值。

    • 示例:

      php 复制代码
      echo end($arr); // 输出 'c'
  3. current(array &$array): mixed

    • 返回当前指针指向的元素值(不移动指针)。

    • 如果指针越界,返回 false

    • 示例:

      php 复制代码
      reset($arr);
      echo current($arr); // 输出 'a'
  4. next(array &$array): mixed

    • 将指针向前移动一位 ,并返回新位置的值。若越界则返回 false

    • 示例:

      php 复制代码
      next($arr);
      echo current($arr); // 输出 'b'
  5. prev(array &$array): mixed

    • 将指针向后移动一位 ,返回新位置的值。若越界返回 false

    • 示例:

      php 复制代码
      prev($arr);
      echo current($arr); // 输出 'a'(指针回到第一个元素)
  6. key(array &$array): int|string|null

    • 返回当前元素的键名。若指针越界,返回 null

    • 示例:

      php 复制代码
      reset($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 判断是否越界:

    php 复制代码
    if (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()

八、注意事项

  1. 引用传递陷阱

    php 复制代码
    $arr = [1, 2];
    $ref = &$arr;
    array_push($ref, 3); // $arr 变为 [1,2,3]
  2. 性能优化

    • 避免在循环内多次调用 count(),提前缓存结果。
    • 优先使用 isset() 而非 array_key_exists()(更快)。
  3. 排序函数副作用

    • sort()rsort() 等会修改原数组并重置键名。
    • 使用 asort()ksort() 保持键关联。
  4. 多维数组合并

    • 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[] = $valuearray_push() 更快,因为它避免了函数调用开销。
  • 简洁性 :PHP 数组天然支持栈的尾部操作(array_poparray_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"

注意事项

  1. 避免使用 array_shift() / array_unshift()
    这些函数操作数组开头,时间复杂度为 O(n),而栈应只操作尾部(O(1) 时间)。
  2. 指针函数与栈的关系
    栈通常只关心顶部元素,end() 可查看栈顶,但无需依赖 next()prev()
  3. 引用传递问题
    直接操作数组时,注意函数参数是否需要引用(例如 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');

注意事项

  1. 避免频繁使用 array_shift()

    直接操作数组头部会导致所有元素重新索引,时间复杂度为 O(n) 。若队列规模较大,建议使用优化的手动实现(如 OptimizedQueue 类)或 PHP 的 SplQueue

  2. 使用 SplQueue 扩展(高效替代方案)

    PHP 的 SplQueue 基于双向链表实现,入队/出队均为 O(1) 时间复杂度:

    php 复制代码
    $queue = new SplQueue();
    $queue->enqueue("A");  // 入队
    $queue->enqueue("B");
    echo $queue->dequeue(); // 输出 "A"
  3. 关联数组优化

    通过维护头尾指针(如 OptimizedQueue 类),可以避免数组重新索引,但需要手动管理逻辑。

  4. 键名溢出问题

    手动优化实现中,若队列长期运行且 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');

注意事项

  1. 避免频繁使用 array_shift()

    直接操作数组头部会导致所有元素重新索引,时间复杂度为 O(n) 。若队列规模较大,建议使用优化的手动实现(如 OptimizedQueue 类)或 PHP 的 SplQueue

  2. 使用 SplQueue 扩展(高效替代方案)

    PHP 的 SplQueue 基于双向链表实现,入队/出队均为 O(1) 时间复杂度:

    php 复制代码
    $queue = new SplQueue();
    $queue->enqueue("A");  // 入队
    $queue->enqueue("B");
    echo $queue->dequeue(); // 输出 "A"
  3. 关联数组优化

    通过维护头尾指针(如 OptimizedQueue 类),可以避免数组重新索引,但需要手动管理逻辑。

  4. 键名溢出问题

    手动优化实现中,若队列长期运行且 rear 超出 PHP 的整数范围,需重置键名(实际场景中罕见)。


总结

  • 简单场景 :直接使用 array_push() + array_shift(),适用于小规模队列。
  • 高频/大数据量 :推荐手动优化实现或 SplQueue
  • 替代方案:使用 Redis 或消息队列服务(如 RabbitMQ)处理分布式或持久化需求。
相关推荐
ServBay4 小时前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954487 小时前
CTF 伪协议
php
BingoGo3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack6 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理6 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php