首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在PHP中优化ArrayIterator实现?

如何在PHP中优化ArrayIterator实现?
EN

Stack Overflow用户
提问于 2017-03-09 14:11:13
回答 2查看 413关注 0票数 2

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

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

代码语言:javascript
复制
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;
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-03-09 15:21:01

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

ArrayIterator实现了ArrayAccess。这意味着您可以向集合中添加如下新项:

代码语言:javascript
复制
$collection = new ColumnCollection;
$collection[$someCollectionObject->name] = $someCollectionObject;

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

代码语言:javascript
复制
$someCollectionObject = $collection["foo"];

如果不想更改客户端代码,只需在offsetSet中重写ColumnCollection:

代码语言:javascript
复制
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()方法添加新元素,只需将其更改为:

代码语言:javascript
复制
public function append($newValue)
{
    parent::offsetSet($newValue->name, $newValue);
}

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

代码语言:javascript
复制
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);
    }
}
票数 2
EN

Stack Overflow用户

发布于 2017-03-09 14:15:13

我将迭代器方法调用替换为数组副本上的一个循环。我认为这可以直接访问内部存储,因为PHP实现了复制即写。本机foreach比调用rewind()valid()current()next()快得多。此外,预计算列对象上的strtolower也有所帮助。这使得性能从35%的循环下降到0.14%的

代码语言:javascript
复制
public function find($name)
{
    $return = null;
    $name = trim(strtolower($name));
    /** @var Column $column */
    foreach ($this->getArrayCopy() as $column) {
        if ($column->nameLower === $name) {
            $return = $column;
            break;
        }
    }

    return $return;
}

此外,还尝试使用@Gordon的建议,使用以名称为键的数组,而不是使用内部存储。以上是一个简单的插入替换的良好工作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42697550

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档