首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 陣列操作大全 (下):進階函數、排序、合併、差集交集、棧隊列操作與最佳實踐

     編輯:PHP 相關知識

PHP 陣列操作大全 (下):進階函數、多維處理、效能優化與實戰模式

承接上篇,本文深入探討陣列進階操作:排序家族、合併差集交集、棧隊列模擬、遞歸處理、SPL 陣列物件、生成器配合、記憶體優化與實戰最佳實踐。

一、排序函數家族完整對比

基本排序 (修改原陣列,返回 bool)

函數排序依據保留鍵名穩定性
sort()值升序否 (重新索引 0...)不穩定
rsort()值降序否不穩定
asort()值升序是PHP 8.0+ 穩定
arsort()值降序是PHP 8.0+ 穩定
ksort()鍵升序是穩定
krsort()鍵降序是穩定
natsort()自然排序 (值)是穩定
natcasesort()自然排序忽略大小寫是穩定
usort()自定義比較 (值)否不穩定
uksort()自定義比較 (鍵)是不穩定
uasort()自定義比較 (值) + 保留鍵是PHP 8.0+ 穩定

自然排序實戰

<?php
$files = ['img1.png', 'img10.png', 'img2.png', 'img20.png', 'img3.png'];

sort($files);      // ['img1.png','img10.png','img2.png','img20.png','img3.png'] 字典序
natsort($files);   // ['img1.png','img2.png','img3.png','img10.png','img20.png'] 自然序

// 版本號排序
$versions = ['1.10.0', '1.2.0', '1.1.0', '2.0.0', '1.9.0'];
natsort($versions); // ['1.1.0','1.2.0','1.9.0','1.10.0','2.0.0']
?>

自定義排序 usort/uasort/uksort

<?php
$users = [
    ['name' => 'Alice', 'age' => 25, 'score' => 90],
    ['name' => 'Bob', 'age' => 30, 'score' => 85],
    ['name' => 'Charlie', 'age' => 25, 'score' => 95]
];

// 多字段排序:年齡升序,分數降序
usort($users, function($a, $b) {
    // 年齡升序
    $ageCmp = $a['age'] <=> $b['age'];
    if ($ageCmp !== 0) return $ageCmp;
    // 年齡相同,分數降序
    return $b['score'] <=> $a['score'];
});

/*
結果:
Alice (25, 90)
Charlie (25, 95)  -> 分數高在前
Bob (30, 85)
*/

// PHP 8.0+ 穩定排序保證:相同年齡分數的保持原序
?>

多維/多陣列排序 array_multisort

<?php
// 場景:多個並行陣列按某欄位排序
$names = ['Alice', 'Bob', 'Charlie'];
$ages = [25, 30, 20];
$scores = [90, 85, 95];

// 按年齡升序排序,並行陣列同步調整
array_multisort($ages, SORT_ASC, $names, $scores);
/*
$ages: [20, 25, 30]
$names: ['Charlie', 'Alice', 'Bob']
$scores: [95, 90, 85]
*/

// 多維陣列排序 (提取欄位)
$data = [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob', 'age' => 30],
    ['name' => 'Charlie', 'age' => 20]
];

$ages = array_column($data, 'age');
array_multisort($ages, SORT_ASC, $data);
// $data 按 age 升序重排
?>

二、陣列合併、差集、交集進階

array_merge 變體

<?php
// array_merge_recursive: 衝突鍵合併為陣列
$a = ['color' => ['red', 'blue'], 'size' => 'M'];
$b = ['color' => ['green'], 'price' => 100];
print_r(array_merge_recursive($a, b));
/*
[color] => Array([0]=>red, [1]=>blue, [2]=>green)
[size] => M
[price] => 100
*/

// array_replace: 後者覆蓋,不重新索引數字鍵
$a = [0 => 'a', 1 => 'b', 'x' => 1];
$b = [1 => 'B', 2 => 'c', 'x' => 2];
print_r(array_replace($a, $b));
// [0=>'a', 1=>'B', 2=>'c', 'x'=>2]

// array_replace_recursive: 遞歸替換
$a = ['db' => ['host' => 'localhost', 'port' => 3306]];
$b = ['db' => ['user' => 'root']];
print_r(array_replace_recursive($a, $b));
// ['db' => ['host'=>'localhost', 'port'=>3306, 'user'=>'root']]
?>

差集與交集家族

函數比較方式返回鍵名保留
array_diff值$a 中不在其它陣列的值$a 的鍵
array_diff_assoc鍵+值$a 中鍵值對不在其它的$a 的鍵
array_diff_key鍵$a 中鍵不在其它的$a 的鍵
array_diff_uassoc鍵+值 (自定義鍵比較)...$a 的鍵
array_intersect值同時在所有陣列的值第一個陣列的鍵
array_intersect_assoc鍵+值...第一個陣列的鍵
array_intersect_key鍵...第一個陣列的鍵
array_intersect_uassoc鍵+值 (自定義鍵比較)...第一個陣列的鍵

實戰:配置差異檢測

<?php
$defaultConfig = [
    'debug' => false,
    'cache' => true,
    'db' => ['host' => 'localhost', 'port' => 3306],
    'allowed_ips' => ['10.0.0.1', '10.0.0.2']
];

$envConfig = [
    'debug' => true,
    'db' => ['host' => 'db-server', 'user' => 'app'],
    'allowed_ips' => ['10.0.0.1', '10.0.0.3', '10.0.0.4']
];

// 扁平化比較 (需自行遞歸)
function arrayDiffRecursive(array $a, array $b): array {
    $diff = [];
    foreach ($a as $k => $v) {
        if (!array_key_exists($k, $b)) {
            $diff[$k] = ['old' => $v, 'new' => null]; // 刪除
        } elseif (is_array($v) && is_array($b[$k])) {
            $subDiff = arrayDiffRecursive($v, $b[$k]);
            if ($subDiff) $diff[$k] = $subDiff;
        } elseif ($v !== $b[$k]) {
            $diff[$k] = ['old' => $v, 'new' => $b[$k]]; // 修改
        }
    }
    // 新增鍵
    foreach ($b as $k => $v) {
        if (!array_key_exists($k, $a)) {
            $diff[$k] = ['old' => null, 'new' => $v];
        }
    }
    return $diff;
}

print_r(arrayDiffRecursive($defaultConfig, $envConfig));
/*
[debug] => [old=>false, new=>true]
[db] => [
    [host] => [old=>localhost, new=>db-server]
    [port] => [old=>3306, new=>null] (刪除)
    [user] => [old=>null, new=>root] (新增)
]
[allowed_ips] => [值差異...]
*/
?>

三、棧/隊列操作 (陣列模擬)

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

// 棧 (LIFO): 後進先出
array_push($stack, 4, 5); // [1,2,3,4,5] 返回新長度 5
$top = array_pop($stack); // 5, $stack=[1,2,3,4]

// 隊列 (FIFO): 先進先出
$queue = [1, 2, 3];
array_push($queue, 4);     // 入隊: [1,2,3,4]
$front = array_shift($queue); // 出隊: 1, $queue=[2,3,4]

// 雙端隊列
array_unshift($queue, 0);  // 頭部插入: [0,2,3,4]
$last = array_pop($queue); // 尾部移除: 4

// 效能注意:array_shift/unshift 需重新索引所有元素 O(n)
// 大量操作建議用 SplDoublyLinkedList 或 SplQueue
?>

SPL 專業資料結構

<?php
// SplStack (棧)
$stack = new SplStack();
$stack->push('a');
$stack->push('b');
echo $stack->pop(); // 'b'
echo $stack->top(); // 'a' (查看不移除)

// SplQueue (隊列)
$queue = new SplQueue();
$queue->enqueue('a');
$queue->enqueue('b');
echo $queue->dequeue(); // 'a'

// SplHeap / SplMaxHeap / SplMinHeap (優先隊列)
$heap = new SplMaxHeap();
$heap->insert(3);
$heap->insert(1);
$heap->insert(5);
while (!$heap->isEmpty()) {
    echo $heap->extract(); // 5, 3, 1
}

// SplFixedArray (固定大小陣列,記憶體效率高)
$arr = new SplFixedArray(1000000);
$arr[0] = 'test';
// 比 array() 節省 ~30% 記憶體
?>

四、遞歸處理多維陣列

遞歸遍歷模板

<?php
function arrayWalkRecursive(array $array, callable $callback, int $depth = 0): array {
    $result = [];
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $result[$key] = arrayWalkRecursive($value, $callback, $depth + 1);
        } else {
            $result[$key] = $callback($value, $key, $depth);
        }
    }
    return $result;
}

// 使用:所有字串修剪
$trimmed = arrayWalkRecursive($data, fn($v) => is_string($v) ? trim($v) : $v);

// 使用:標記深度
$withDepth = arrayWalkRecursive($data, fn($v, $k, $d) => ['value' => $v, 'depth' => $d]);
?>

扁平化多維陣列

<?php
function flattenArray(array $array, string $separator = '.'): array {
    $result = [];
    
    function _flatten(array $array, string $prefix, array &$result, string $separator) {
        foreach ($array as $key => $value) {
            $newKey = $prefix ? $prefix . $separator . $key : $key;
            if (is_array($value)) {
                _flatten($value, $newKey, $result, $separator);
            } else {
                $result[$newKey] = $value;
            }
        }
    }
    
    _flatten($array, '', $result, $separator);
    return $result;
}

$nested = [
    'user' => [
        'name' => 'Alice',
        'address' => ['city' => 'Taipei', 'zip' => '100']
    ],
    'settings' => ['theme' => 'dark']
];

print_r(flattenArray($nested));
/*
[user.name] => Alice
[user.address.city] => Taipei
[user.address.zip] => 100
[settings.theme] => dark
*/
?>

五、陣列與生成器配合 (記憶體優化)

<?php
// 大數據集分塊處理
function chunkedArrayGenerator(iterable $source, int $chunkSize): Generator {
    $chunk = [];
    foreach ($source as $item) {
        $chunk[] = $item;
        if (count($chunk) >= $chunkSize) {
            yield $chunk;
            $chunk = [];
        }
    }
    if ($chunk) yield $chunk;
}

// 使用:處理 100 萬條記錄,每 1000 條一批
foreach (chunkedArrayGenerator($largeDataset, 1000) as $batch) {
    processBatch($batch); // 釋放上一批記憶體
}

// 惰性映射
function mapGenerator(iterable $source, callable $fn): Generator {
    foreach ($source as $key => $value) {
        yield $key => $fn($value, $key);
    }
}

// 組合使用
$processed = mapGenerator(
    chunkedArrayGenerator($data, 100),
    fn($batch) => array_map('processItem', $batch)
);
?>

六、記憶體優化最佳實踐

場景問題優化方案
大陣列複製foreach 產生副本用引用 &$v 或 SplFixedArray
超大陣列排序記憶體峰值高分塊排序 + 歸併、或外部排序
頻繁 array_push/pop擴容/縮容開銷預分配 SplFixedArray、或雙端隊列
多維陣列深層複製遞歸複製慢序列化/unserialize 或 var_export+eval
陣列作為映射表鍵名為長字串用整數 ID 映射、或 SplObjectStorage

大陣列去重優化

<?php
// 百萬級陣列去重
function fastUnique(array $array): array {
    // 方法 1: array_unique (內建,但會保留鍵名)
    return array_values(array_unique($array));
    
    // 方法 2: array_flip 兩次 (更快,但值必須能作鍵名)
    // return array_keys(array_flip($array));
    
    // 方法 3: 手動哈希表 (最可控)
    $seen = [];
    $result = [];
    foreach ($array as $value) {
        $key = is_scalar($value) ? $value : serialize($value);
        if (!isset($seen[$key])) {
            $seen[$key] = true;
            $result[] = $value;
        }
    }
    return $result;
}
?>

七、實戰模式:配置管理、數據轉換、API 回應構建

配置合併模式

<?php
class Config {
    private array $data = [];
    
    public function load(array $config): self {
        $this->data = $this->deepMerge($this->data, $config);
        return $this;
    }
    
    public function get(string $path, $default = null) {
        $keys = explode('.', $path);
        $current = $this->data;
        foreach ($keys as $key) {
            if (!is_array($current) || !array_key_exists($key, $current)) {
                return $default;
            }
            $current = $current[$key];
        }
        return $current;
    }
    
    private function deepMerge(array $a, array $b): array {
        $result = $a;
        foreach ($b as $k => $v) {
            if (isset($result[$k]) && is_array($result[$k]) && is_array($v)) {
                $result[$k] = $this->deepMerge($result[$k], $v);
            } else {
                $result[$k] = $v;
            }
        }
        return $result;
    }
}

$config = new Config();
$config->load(require 'config/default.php')
       ->load(require 'config/local.php')
       ->load($_ENV); // 環境變量最高優先級

echo $config->get('db.host', 'localhost');
?>

API 回應構建器

<?php
class ApiResponse {
    private array $data = [];
    private int $code = 200;
    private string $message = '';
    private array $meta = [];
    
    public static function success($data = null, string $message = ''): self {
        $resp = new self();
        $resp->data = $data ?? [];
        $resp->message = $message;
        return $resp;
    }
    
    public static function error(int $code, string $message, $data = null): self {
        $resp = new self();
        $resp->code = $code;
        $resp->message = $message;
        $resp->data = $data ?? [];
        return $resp;
    }
    
    public function withMeta(array $meta): self {
        $this->meta = $meta;
        return $this;
    }
    
    public function toArray(): array {
        return array_filter([
            'code' => $this->code,
            'message' => $this->message,
            'data' => $this->data,
            'meta' => $this->meta ?: null
        ], fn($v) => $v !== null && $v !== '');
    }
    
    public function send(): void {
        http_response_code($this->code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($this->toArray(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
}

// 使用
ApiResponse::success($users, '獲取成功')
    ->withMeta(['total' => 100, 'page' => 1, 'per_page' => 20])
    ->send();
?>

八、常見陷阱總結

  1. foreach 引用殘留:`foreach ($arr as &$v) { ... } unset($v);` 必須 unset
  2. array_merge 數字鍵重新索引:字串鍵合併用 `+` 運算子
  3. in_array 嚴格模式:第三參數 `true` 避免 `'123' == 123` 誤判
  4. array_search 返回 0 或 false:必須用 `!== false` 判斷
  5. count() 遞歸模式:`COUNT_RECURSIVE` 計算所有子元素,非層數
  6. 空陣列排序:`sort([])` 返回 `true`,不報錯
  7. array_column 重複鍵:後者覆蓋前者,需配合 `array_combine` 保留所有

PHP 陣列功能極其豐富,掌握核心函數語義差異、效能特徵、記憶體行為,配合 SPL 專業結構與生成器模式,能應對從簡單業務到高併發大數據的各種場景。建立團隊統一的陣列操作規範與工具庫,能顯著提升代碼質量與開發效率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved