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 按键排序遍历,不是按插入顺序。需要插入顺序的场景使用原生关联数组。
最佳实践
- 需要按键排序时用 Map:自动排序
- 键值对遍历用 Pair:
foreach ($map as $pair) - 批量添加用 putAll:比循环 put 更高效
- 差集/交集用 diff/intersect:配置对比
- 序列化用 toArray():JSON 输出前转换