Skip to content

Map

概述

Ds\Map 是一种键值对集合,类似于 PHP 原生关联数组,但具有按键排序、类型安全和更好的性能等优势。Map 中的键是唯一的,值可以重复。

基础概念

特性

特性说明
键值映射每个键对应一个值
键唯一重复键会覆盖
有序按键的插入顺序或排序顺序
类型安全键和值可以是任意类型
按键排序支持按键排序

Map vs 原生关联数组

特性Map原生数组
键顺序按键值排序按插入顺序
键类型任意(对象等)仅 int/string
重复键自动覆盖数字键自动转换
函数式操作map/filter/reduce需外部函数

语法与代码

创建 Map

php
<?php

declare(strict_types=1);

use Ds\Map;

// 从数组创建
$map = new Map(['name' => 'Alice', 'age' => 30]);

// 空Map
$empty = new Map();

// 迭代创建
$map = new Map();
$map->put('a', 1);
$map->put('b', 2);

基本操作

php
<?php

declare(strict_types=1);

use Ds\Map;

$map = new Map(['name' => 'Alice', 'age' => 30, 'email' => 'a@b.com']);

// 读取
echo $map->get('name');       // Alice
echo $map->get('unknown');     // null(默认返回 null)
echo $map->get('unknown', 'N/A');  // N/A(指定默认值)

// 修改/添加
$map->put('age', 31);         // 修改
$map->put('role', 'admin');   // 添加

// 检查
echo $map->has('name');        // true
echo $map->has('unknown');     // false

// 移除
$map->remove('email');
// ['name' => 'Alice', 'age' => 31, 'role' => 'admin']

// 统计
echo $map->count();    // 3
echo $map->isEmpty();  // false

// first / last(按键排序后的第一个/最后一个)
echo $map->first()->value;  // Alice(键 'name' 最小)
echo $map->last()->value;   // admin(键 'role' 最大)

键值对遍历

php
<?php

declare(strict_types=1);

use Ds\Map;

$map = new Map(['name' => 'Alice', 'age' => 30, 'role' => 'admin']);

// 遍历键值对(Pair 对象)
foreach ($map as $pair) {
    echo "{$pair->key}: {$pair->value}\n";
}

// 获取键和值的集合
$keys = $map->keys();     // Ds\Set ['name', 'age', 'role']
$values = $map->values();  // Ds\Vector ['Alice', 30, 'admin']

// 使用 keys() 和 values() 遍历
foreach ($map->keys() as $key) {
    echo $key . ': ' . $map->get($key) . "\n";
}

函数式操作

php
<?php

declare(strict_types=1);

use Ds\Map;

$scores = new Map(['Alice' => 85, 'Bob' => 92, 'Charlie' => 78]);

// map - 转换值
$curved = $scores->map(fn(int $score): int => min(100, $score + 5));
// ['Alice' => 90, 'Bob' => 97, 'Charlie' => 83]

// filter - 按值筛选
$passed = $scores->filter(fn(int $score): bool => $score >= 80);
// ['Alice' => 85, 'Bob' => 92]

// reduce - 归约
$total = $scores->reduce(fn(int $carry, int $score): int => $carry + $score, 0);
// 255

// sort / ksort
$sorted = $scores->copy();
$sorted->sort(fn(int $a, int $b): int => $a <=> $b);  // 按值排序
$sorted->ksort();  // 按键排序

// diff - 差集
$map1 = new Map(['a' => 1, 'b' => 2, 'c' => 3]);
$map2 = new Map(['b' => 20, 'c' => 3, 'd' => 4]);
$diff = $map1->diff($map2);
// ['a' => 1, 'b' => 2](键相同但值不同的)

合并与交集

php
<?php

declare(strict_types=1);

use Ds\Map;

$map1 = new Map(['a' => 1, 'b' => 2]);
$map2 = new Map(['b' => 20, 'c' => 3]);

// union - 合并(相同键取后者)
$union = $map1->union($map2);
// ['a' => 1, 'b' => 20, 'c' => 3]

// intersect - 交集(相同键取前者)
$intersect = $map1->intersect($map2);
// ['b' => 2]

// putAll - 批量添加
$target = new Map(['a' => 1]);
$target->putAll(['b' => 2, 'c' => 3]);
// ['a' => 1, 'b' => 2, 'c' => 3]

toArray 与序列化

php
<?php

declare(strict_types=1);

use Ds\Map;

$map = new Map(['name' => 'Alice', 'age' => 30]);

// toArray
$arr = $map->toArray();
// ['name' => 'Alice', 'age' => 30]

// jsonSerialize
echo json_encode($map);
// {"name":"Alice","age":30}

// copy
$copy = $map->copy();

详细说明

Map 按键排序

Map 内部始终按键排序。这意味着遍历顺序是按键的自然顺序,而非插入顺序。这是 Map 与原生关联数组最重要的区别。

php
<?php

declare(strict_types=1);

use Ds\Map;

$map = new Map();
$map->put('zebra', 1);
$map->put('apple', 2);
$map->put('banana', 3);

foreach ($map as $pair) {
    echo $pair->key . "\n";
}
// 输出: apple, banana, zebra(按键排序,非插入顺序)

按键排序特性

Map 始终按键排序,不保留插入顺序。如果需要保持插入顺序,请使用原生关联数组或 Ds\Sequence

实战示例

配置管理器

php
<?php

declare(strict_types=1);

use Ds\Map;

class ConfigManager
{
    private Map $config;

    public function __construct(array $defaults = [])
    {
        $this->config = new Map($defaults);
    }

    public function get(string $key, mixed $default = null): mixed
    {
        return $this->config->get($key, $default);
    }

    public function set(string $key, mixed $value): void
    {
        $this->config->put($key, $value);
    }

    public function has(string $key): bool
    {
        return $this->config->has($key);
    }

    public function all(): array
    {
        return $this->config->toArray();
    }

    public function merge(array $overrides): void
    {
        foreach ($overrides as $key => $value) {
            $this->config->put($key, $value);
        }
    }
}

$cm = new ConfigManager(['debug' => false, 'cache' => true]);
$cm->set('timezone', 'Asia/Shanghai');
$cm->merge(['debug' => true, 'version' => '2.0']);
print_r($cm->all());

Map 的高级操作

php
<?php

declare(strict_types=1);

use Ds\Map;

// Map 的 ksort 和 sort
$scores = new Map(['Alice' => 85, 'Bob' => 92, 'Charlie' => 78]);

// 按值排序
$byValue = $scores->copy();
$byValue->sort(fn(int $a, int $b): int => $b <=> $a);
// 按值降序

// 按键排序
$byKey = $scores->copy();
$byKey->ksort();
// 按键字母顺序

// Map 的 first 和 last 返回 Pair
$first = $scores->first();
echo $first->key . ': ' . $first->value;  // Alice: 85

$last = $scores->last();
echo $last->key . ': ' . $last->value;    // Charlie: 78

// Map 的 has 和 get
$map = new Map(['a' => 1, 'b' => 2, 'c' => 3]);
echo $map->has('a');        // true
echo $map->get('a');        // 1
echo $map->get('z', 'N/A'); // N/A(默认值)

Map 用于配置存储

php
<?php

declare(strict_types=1);

use Ds\Map;

class ConfigStore
{
    private Map $config;

    public function __construct(array $defaults = [])
    {
        $this->config = new Map($defaults);
    }

    public function set(string $key, mixed $value): void
    {
        $this->config->put($key, $value);
    }

    public function get(string $key, mixed $default = null): mixed
    {
        return $this->config->get($key, $default);
    }

    public function remove(string $key): void
    {
        $this->config->remove($key);
    }

    public function keys(): array
    {
        return $this->config->keys()->toArray();
    }

    public function all(): array
    {
        return $this->config->toArray();
    }

    public function toArray(): array
    {
        return $this->config->toArray();
    }
}

$store = new ConfigStore(['debug' => false, 'cache' => true]);
$store->set('timezone', 'Asia/Shanghai');
$store->remove('cache');
print_r($store->all());

Map 的键排序

Map 始终按键排序。如果需要保持插入顺序,使用原生关联数组或自定义 OrderedMap 类。

Map 的 JSON 输出与序列化

php
<?php

declare(strict_types=1);

use Ds\Map;

$map = new Map(['name' => 'Alice', 'age' => 30, 'scores' => [90, 85]]);

// JSON 序列化
echo json_encode($map);
// {"name":"Alice","age":30,"scores":[90,85]}

// serialize/unserialize
$serialized = serialize($map);
$restored = unserialize($serialized);
echo $restored->get('name');  // Alice

// 注意:Map 的键是有序的(按键排序)
$map = new Map(['z' => 1, 'a' => 2, 'm' => 3]);
echo json_encode($map);
// {"a":2,"m":3,"z":1}(按键排序输出)

注意事项

遍历顺序

Map 按键排序遍历,不是按插入顺序。需要插入顺序的场景使用原生关联数组。

最佳实践

  1. 需要按键排序时用 Map:自动排序
  2. 键值对遍历用 Pairforeach ($map as $pair)
  3. 批量添加用 putAll:比循环 put 更高效
  4. 差集/交集用 diff/intersect:配置对比
  5. 序列化用 toArray():JSON 输出前转换

参考链接