PHP 接口 IteratorAggregate vs Iterator?

2022-08-30 17:00:48

是一个用于创建外部迭代器的接口IteratorAggregate

class myData implements IteratorAggregate
{
    public $property1 = "Public property one";
    public $property2 = "Public property two";
    public $property3 = "Public property three";

    public function __construct()
    {
        $this->property4 = "last property";
    }

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

$obj = new myData;

您将能够使用以下命令遍历对象:foreach

foreach($obj as $key => $value) {
    var_dump($key, $value);
    echo "\n";
}

While 是外部迭代器或对象的接口,可以在内部自行迭代Iterator

class myIterator implements Iterator
{
    private $position = 0;
    private $array = array('one', 'two', 'three');

    function rewind()
    {
        $this->position = 0;
    }

    function current()
    {
        return $this->array[$this->position];
    }

    function key()
    {
        return $this->position;
    }

    function next()
    {
        ++$this->position;
    }

    function valid()
    {
        return isset($this->array[$this->position]);
    }
}

同样,你可以用基本上相同的方式遍历它:

$it = new myIterator;

foreach($it as $key => $value) {
    var_dump($key, $value);
    echo "\n";
}

那么,任何人都可以解释为什么我们需要两个接口以及它们之间的区别是什么?


答案 1

我假设适用于想要节省时间的情况,以及需要对对象的迭代进行精细控制的情况。例如,它允许您添加有关 的自定义异常或失败、缓存(如果您循环访问通过网络获取数据的内容),在返回值之前对其进行预处理。IteratorAggregateIteratornext()key()prev()


答案 2

IteratorAggregate是实现迭代器的一种简单方法。缺点是您无法添加 、 等方法,因为它们在正常遍历期间不会被调用。next()key()foreach

如果需要实现自定义方法,则需要实现 或 (更容易) 扩展 .OuterIteratorIteratorIterator

优点是速度快,它比它的替代品快得多。它的问题是...尽管名称如此,但它不是迭代器,而是可遍历的(所以再次,没有下一个,键,当前,有效,倒带方法)。IteratorAggregate

这是一个简单的基准测试,我在我可怜的笔记本电脑上运行,每个,每次都有100万次迭代,并且使用和:(还要注意Ag aggregate下一个方法中的回声:没有打印“x”,证明它永远不会被调用)IteratorAggregateIteratorIteratorOuterIteratoriterator_to_arrayforeach

$ repeat 3 php benchIterator.php
------------------------------------
Outer_ToArray: 569.61703300476 ms
Aggregate_ToArray: 552.38103866577 ms
IteratorIt_ToArray: 546.95200920105 ms
Outer_Foreach: 1679.8989772797 ms
IteratorIt_Foreach: 1019.6850299835 ms
Aggregate_Foreach: 367.35391616821 ms
------------------------------------
Outer_ToArray: 570.75309753418 ms
Aggregate_ToArray: 544.40784454346 ms
IteratorIt_ToArray: 555.06300926208 ms
Outer_Foreach: 1682.2130680084 ms
IteratorIt_Foreach: 988.21592330933 ms
Aggregate_Foreach: 356.41598701477 ms
------------------------------------
Outer_ToArray: 566.06101989746 ms
Aggregate_ToArray: 543.1981086731 ms
IteratorIt_ToArray: 546.28610610962 ms
Outer_Foreach: 1663.2289886475 ms
IteratorIt_Foreach: 995.28503417969 ms
Aggregate_Foreach: 356.16087913513 ms

以下是我用于基准测试的代码:

<?php

class Aggregate implements \IteratorAggregate
{
    protected $var;

    public function __construct($var = null)
    {
        if (is_array($var)) {
            $this->var = new ArrayIterator($var);
        }
        if ($var instanceof \Traversable) {
            $this->var = $var;
        }
    }
    public function next()
    {
        echo 'x';
    }
    public function toArray()
    {
        return iterator_to_array($this->var, true);
    }

    public function getIterator()
    {
        return $this->var;
    }

}

class Outer implements \OuterIterator
{
    protected $var;

    public function __construct($var = null)
    {
        if (is_array($var)) {
            $this->var = new ArrayIterator($var);
        }
        if ($var instanceof \Traversable) {
            $this->var = $var;
        }
    }

    public function toArray()
    {
        return iterator_to_array($this->var, true);
    }

    public function getInnerIterator()
    {
        return $this->var;
    }

    public function current()
    {
        return $this->var->current();
    }
    public function next()
    {
        $this->var->next();
    }
    public function key()
    {
        return  $this->var->key();
    }
    public function valid()
    {
        return  $this->var->valid();

    }
    public function rewind()
    {
     $this->var->rewind();

    }
}


class IteratorIt extends IteratorIterator
{
    public function __construct($var = null)
    {
        if (is_array($var)) {
            $var = new ArrayIterator($var);

        }
        parent::__construct($var);

    }

    public function toArray()
    {
        return iterator_to_array($this->getInnerIterator(), true);
    }
    public function getIterator()
    {
        return $this->getInnerIterator();
    }
}

function bench($name, $test)
{
    echo "$name: ";
    $start = microtime(true);
    $test();
    $time = microtime(true);
    $time -= $start;

    echo ($time * 1000) . ' ms' . PHP_EOL;
}

$max = 1e6;
$array = range (1, 1e6);
$testSuites = [
    'Outer_ToArray' => function () use ($max, $array) {
        $iterator = new Outer($array);
        $r = $iterator->toArray();
    },
    'Aggregate_ToArray' => function () use ($max, $array) {
        $iterator = new Aggregate($array);
        $r = $iterator->toArray();
    },
    'IteratorIt_ToArray' => function () use ($max, $array) {
        $iterator = new IteratorIt($array);
        $r = $iterator->toArray();
    },
    'Outer_Foreach' => function () use ($max, $array) {
        $iterator = new Outer($array);
        foreach ($iterator as $k => $v)
        {

        }
    },
    'IteratorIt_Foreach' => function () use ($max, $array) {
        $iterator = new IteratorIt($array);
        foreach ($iterator as $k => $v)
        {

        }
    },
    'Aggregate_Foreach' => function () use ($max, $array) {
        $iterator = new Aggregate($array);
        foreach ($iterator as $k => $v)
        {

        }
    },
];

echo '------------------------------------'.PHP_EOL;
foreach ($testSuites as $name => $test) {
    bench($name, $test);
}