我有一个运行时间较长的PHP守护程序,该守护程序带有扩展ArrayIterator的集合类。它包含一组自定义的Column对象,通常少于1000个。通过xdebug分析器运行它,我发现find方法消耗了大约35%的周期。

如何以一种优化的方式在内部迭代这些项目?

class ColumnCollection extends \ArrayIterator
{
    public function find($name)
    {
        $return = null;
        $name = trim(strtolower($name));
        $this->rewind();
        while ($this->valid()) {
            /** @var Column $column */
            $column = $this->current();
            if (strtolower($column->name) === $name) {
                $return = $column;
                break;
            }
            $this->next();
        }
        $this->rewind();

        return $return;
    }
}

最佳答案

您的find()方法显然只返回带有查询的$name的第一个Column对象。在这种情况下,按名称索引Array可能是有意义的,例如以对象的名称作为键存储对象。然后,您的查找将成为O(1)调用。

ArrayIterator实现ArrayAccess。这意味着您可以像这样将新项目添加到您的收藏夹:

$collection = new ColumnCollection;
$collection[$someCollectionObject->name] = $someCollectionObject;


并通过方括号符号检索它们:

$someCollectionObject = $collection["foo"];


如果您不想更改客户端代码,只需在ColumnCollection中覆盖offsetSet即可:

public function offsetSet($index, $newValue)
{
    if ($index === null && $newValue instanceof Column) {
        return parent::offsetSet($newValue->name, $newValue);
    }
    return parent::offsetSet($index, $newValue);
}


这样,执行$collection[] = $column将自动按名称添加$ column。有关演示,请参见http://codepad.org/egAchYpk

如果使用append()方法添加新元素,只需将其更改为:

public function append($newValue)
{
    parent::offsetSet($newValue->name, $newValue);
}


但是,ArrayAccess比本机数组访问慢,因此您可能需要将ColumnCollection更改为以下内容:

class ColumnCollection implements IteratorAggregate
{
    private $columns = []; // or SplObjectStorage

    public function add(Column $column) {
        $this->columns[$column->name] = $column;
    }

    public function find($name) {
        return isset($this->data[$name]) ? $this->data[$name] : null;
    }

    public function getIterator()
    {
        return new ArrayIterator($this->data);
    }
}

关于php - 如何优化PHP中的ArrayIterator实现?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42697550/

10-12 17:12
查看更多