dqmq0654 2017-03-09 14:11
浏览 51
已采纳

如何在PHP中优化ArrayIterator实现?

I have a long running PHP daemon with a collection class that extends ArrayIterator. This holds a set of custom Column objects, typically less than 1000. Running it through the xdebug profiler I found my find method consuming about 35% of the cycles.

How can I internally iterate over the items in an optimized way?

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;
    }
}
  • 写回答

2条回答 默认 最新

  • dongshi9719 2017-03-09 15:21
    关注

    Your find() method apparently just returns the first Column object with the queried $name. In that case, it might make sense to index the Array by name, e.g. store the object by it's name as the key. Then your lookup becomes a O(1) call.

    ArrayIterator implements ArrayAccess. This means you can add new items to your Collection like this:

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

    and also retrieve them via the square bracket notation:

    $someCollectionObject = $collection["foo"];
    

    If you don't want to change your client code, you can simply override offsetSet in your ColumnCollection:

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

    This way, doing $collection[] = $column would automatically add the $column by name. See http://codepad.org/egAchYpk for a demo.

    If you use the append() method to add new elements, you just change it to:

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

    However, ArrayAccess is slower than native array access, so you might want to change your ColumnCollection to something like this:

    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);
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮
  • ¥15 ads仿真结果在圆图上是怎么读数的
  • ¥20 Cotex M3的调试和程序执行方式是什么样的?
  • ¥20 java项目连接sqlserver时报ssl相关错误