第八章-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, "[email protected]"];
    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)处理分布式或持久化需求。
相关推荐
Hxyle3 分钟前
c++设计模式
开发语言·c++·设计模式
blammmp25 分钟前
算法专题四:前缀和
java·开发语言·算法
www_pp_28 分钟前
# 创建一个功能完备的计算器应用:使用PyQt5和Python
开发语言·python·qt
Aimyon_361 小时前
Java复习笔记-基础
java·开发语言·笔记
androidwork1 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
codefly-xtl1 小时前
责任链设计模式
java·开发语言·设计模式
非晓为骁2 小时前
【Go】优化文件下载处理:从多级复制到零拷贝流式处理
开发语言·后端·性能优化·golang·零拷贝
北极象2 小时前
Golang中集合相关的库
开发语言·后端·golang
海尔辛2 小时前
学习黑客 MAC 地址深入了解
学习·macos·php
小浪学编程2 小时前
C#学习7_面向对象:类、方法、修饰符
开发语言·学习·c#