Skip to content

Vector

概述

Ds\Vector 是一个有序的连续值序列,类似于 PHP 原生索引数组,但具有更好的内存局部性和性能优势。Vector 使用连续内存存储,支持 O(1) 的索引访问和末尾操作。

基础概念

特性

特性说明
有序元素按插入顺序排列
自动索引从 0 开始的整数索引
连续内存类似 C 数组的内存布局
类型安全值可以是任意类型
可序列化支持 toArray()jsonSerialize()

时间复杂度

操作复杂度
get($index)O(1)
set($index, $value)O(1)
push(...$values)O(1) 摊还
pop()O(1)
insert($index, ...$values)O(n)
remove($index)O(n)
shift() / unshift()O(n)

语法与代码

创建 Vector

php
<?php

declare(strict_types=1);

use Ds\Vector;

// 从数组创建
$vector = new Vector([1, 2, 3, 4, 5]);

// 空向量
$empty = new Vector();

// 从其他集合创建
$set = new \Ds\Set([1, 2, 3]);
$vector = new Vector($set->toArray());

基本操作

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector(['a', 'b', 'c']);

// 读取
echo $vector->get(0);    // a
echo $vector->get(-1);   // c(负索引,PHP 8.0+)
echo $vector->first();    // a
echo $vector->last();     // c

// 修改
$vector->set(1, 'B');
echo $vector->get(1);     // B

// 追加
$vector->push('d', 'e');
// ['a', 'B', 'c', 'd', 'e']

// 移除末尾
$vector->pop();  // 移除 'e'

// 统计
echo $vector->count();  // 4
echo $vector->isEmpty(); // false

插入与删除

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector([1, 2, 3, 4, 5]);

// insert - 在指定位置插入
$vector->insert(2, 10, 20);
// [1, 2, 10, 20, 3, 4, 5]

// remove - 移除指定位置
$vector->remove(2);
// [1, 2, 20, 3, 4, 5]

// shift - 移除第一个
$vector->shift();
// [2, 20, 3, 4, 5](O(n) 操作)

// unshift - 在开头插入
$vector->unshift(0);
// [0, 2, 20, 3, 4, 5](O(n) 操作)

// 清空
$vector->clear();

查找与排序

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector([3, 1, 4, 1, 5, 9, 2, 6]);

// contains - 是否包含值
echo $vector->contains(5);   // true
echo $vector->contains(99);  // false

// find - 查找值(返回键或 false)
$key = $vector->find(5);
echo $key;  // 4

// sort - 原地排序
$sorted = $vector->copy();
$sorted->sort();
// [1, 1, 2, 3, 4, 5, 6, 9]

// reverse - 原地反转
$sorted->reverse();
// [9, 6, 5, 4, 3, 2, 1, 1]

// sorted - 返回排序后的新 Vector(不修改原 Vector)
$newVector = $vector->sorted();

函数式操作

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

// map - 转换
$squared = $vector->map(fn(int $n): int => $n ** 2);
// [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

// filter - 筛选
$evens = $vector->filter(fn(int $n): bool => $n % 2 === 0);
// [2, 4, 6, 8, 10]

// reduce - 归约
$sum = $vector->reduce(fn(int $carry, int $n): int => $carry + $n, 0);
// 55

// join - 连接为字符串
$words = new Vector(['Hello', 'World', 'PHP']);
echo $words->join(', ');  // Hello, World, PHP

// slice - 切片
$sliced = $vector->slice(2, 3);
// [3, 4, 5]

toArray 与序列化

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector([1, 2, 3]);

// toArray - 转为原生数组
$arr = $vector->toArray();
// [1, 2, 3]

// jsonSerialize - JSON 序列化
echo json_encode($vector);
// [1, 2, 3]

// copy - 创建副本
$copy = $vector->copy();
$copy->push(4);
echo $vector->count();  // 3(不受影响)

详细说明

Vector vs 原生索引数组

php
<?php

declare(strict_types=1);

// Vector 的优势:
// 1. 内存连续,缓存友好
// 2. push/pop 是 O(1)(原生数组 array_push 也是 O(1))
// 3. shift/unshift 比 array_shift 快(但仍然是 O(n))
// 4. 提供丰富的函数式方法(map/filter/reduce)
// 5. 类型安全

// 劣势:
// 1. 需要安装 DS 扩展
// 2. 不支持字符串键
// 3. 不能直接 json_encode(需要 toArray)
// 4. 不支持 [] 语法

使用建议

Vector 适合大量数据的顺序存储和函数式操作。如果只是简单的数组操作,原生数组足够。

实战示例

数据流处理

php
<?php

declare(strict_types=1);

use Ds\Vector;

class DataPipeline
{
    private Vector $pipeline;

    public function __construct(array $data)
    {
        $this->pipeline = new Vector($data);
    }

    public function filter(callable $callback): self
    {
        $this->pipeline = new Vector(
            array_values(array_filter($this->pipeline->toArray(), $callback))
        );
        return $this;
    }

    public function map(callable $callback): self
    {
        $this->pipeline = new Vector(
            array_map($callback, $this->pipeline->toArray())
        );
        return $this;
    }

    public function take(int $n): self
    {
        $this->pipeline = $this->pipeline->slice(0, $n);
        return $this;
    }

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

$result = (new DataPipeline(range(1, 100)))
    ->filter(fn(int $n): bool => $n % 3 === 0)
    ->map(fn(int $n): int => $n ** 2)
    ->take(5)
    ->collect();

print_r($result);
// [9, 36, 81, 144, 225]

Vector 的迭代器

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector(['a', 'b', 'c', 'd', 'e']);

// reverse 迭代
$reversed = $vector->copy()->reverse();
foreach ($reversed as $value) {
    echo $value . ' ';
}
// e d c b a

// 使用 ArrayAccess 接口
echo $vector[0];      // a
echo $vector[4];      // e
isset($vector[2]);    // true
isset($vector[10]);   // false

// 遍历时修改(需要引用)
$vector = new Vector([1, 2, 3, 4, 5]);
foreach ($vector as $key => $value) {
    $vector->set($key, $value * 10);
}
// [10, 20, 30, 40, 50]

Vector 与原生数组互转

php
<?php

declare(strict_types=1);

use Ds\Vector;

// 原生数组 -> Vector
$native = [1, 2, 3, 4, 5];
$vector = new Vector($native);

// Vector -> 原生数组
$back = $vector->toArray();

// 在混合代码中使用
function processData(Vector $data): Vector
{
    return $data->filter(fn(int $n): bool => $n > 2)
               ->map(fn(int $n): int => $n ** 2);
}

// 从原生数组调用
$result = processData(new Vector([1, 2, 3, 4, 5]))
    ->toArray();
// [9, 16, 25]

选择 Vector 的场景

  1. 需要大量 push/pop 操作的有序数据
  2. 需要函数式链式操作(map/filter/reduce)
  3. 需要高性能的连续内存存储
  4. 数据量较大且频繁修改时

Vector 的 JSON 输出

php
<?php

declare(strict_types=1);

use Ds\Vector;

$vector = new Vector(['name' => 'Alice', 'scores' => [90, 85, 95]]);

// Vector 实现了 JsonSerializable
$json = json_encode($vector);
echo $json;
// ["name":"Alice","scores":[90,85,95]]

// 嵌套 Vector
$nested = new Vector([
    new Vector([1, 2, 3]),
    new Vector([4, 5, 6]),
]);
echo json_encode($nested);
// [[1,2,3],[4,5,6]]

注意事项

越界访问

get() 越界时抛出 OutOfRangeException。使用前用 has()offsetExists() 检查。

最佳实践

  1. 大数据量用 Vector:内存局部性好
  2. 函数式操作用 Vector:map/filter/reduce 链式调用
  3. 序列化用 toArray():JSON 输出前转换
  4. 越界检查用 contains/find:避免 get 越界
  5. copy 创建独立副本:需要保留原数据时

参考链接