PHP接口IteratorAggregate vs Iterator?

时间:2012-11-29 11:21:20

标签: php arrays iterator traversal

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";
}

虽然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";
}

所以有人可以解释为什么我们需要两个接口,它们之间的区别是什么?

7 个答案:

答案 0 :(得分:16)

我认为IteratorAggregate适用于您希望节省时间的情况,而Iterator适用于您需要对通过对象进行迭代的精细控制的情况。例如,它允许您在next()key()prev()失败,缓存(如果您遍历通过网络获取数据的内容)上添加自定义异常,在返回之前预处理值。< / p>

答案 1 :(得分:12)

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

如果您需要实施自定义方法,则需要实施OuterIterator或(更简单)来扩展IteratorIterator

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

这是一个简单的基准测试我在我的可怜笔记本电脑上运行,每IteratorAggregateIteratorIteratorOuterIterator使用iterator_to_arrayforeach进行100万次迭代echo(另请注意聚合next方法中的$ 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 :不打印&#39; x&#39;打印,证明它永远不会被调用)

<?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);
}

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

{{1}}

答案 2 :(得分:6)

它们之间的区别是什么?

名称的差异(&#34;聚合&#34;)。

Aggregate是什么意思?

  

聚合状态通过禁止外部对象保持对其成员的引用来保证聚合内所做更改的一致性。   (wikipedia)

TC代码:

class myData implements IteratorAggregate {//your implementation}

class myIterator implements Iterator {//your implementation}

这里是聚合清晰度的代码:

$renderFunction = function($iterator) {
    foreach($iterator as $key => $value) {
        echo "$key: $value\n";
        foreach($iterator as $key => $value) {
           echo "    $key: $value\n";
        }
    }
};

$renderFunction(new myData); // Aggregate on
$renderFunction(new myIterator); // Aggregate off

您对案件的期望是什么?

https://3v4l.org/JHDNQ

property1: Public property one
    property1: Public property one
    property2: Public property two
    property3: Public property three
    property4: last property
property2: Public property two
    property1: Public property one
    property2: Public property two
    property3: Public property three
    property4: last property
property3: Public property three
    property1: Public property one
    property2: Public property two
    property3: Public property three
    property4: last property
property4: last property
    property1: Public property one
    property2: Public property two
    property3: Public property three
    property4: last property

VS

0: one
    0: one
    1: two
    2: three

答案 3 :(得分:6)

通过实现IteratorAggregate,我们委派实现迭代器函数的工作(key()next()current()valid()rewind())到其他课程(仅通过实施getIterator()

这样,它可以帮助我们在OOP中实现单独的关注。

答案 4 :(得分:1)

那么谁能解释为什么我们需要两个接口,它们之间有什么区别?

PHP中存在一个抽象的基本接口Traversable

如果类实现了Traversable,则可以在foreach loop中调用它并对其进行迭代。但是,由于类是抽象的基本接口,因此无法直接实现Traversable

相反,类可以实现IteratorAggregateIterator。因此,它们都有一个共同点,即如果要使对象可通过foreach loop进行调用,则需要实现其中一个(或其中的任何子代)。

如果要控制指针并给出精确的指令Iterator,则应实现how,它应进行迭代。如果要使用Iterator中使用的现有foreach loop,则可以实现IteratorAggregate

您总是可以使用IteratorIteratorIteratorAggregate转换回Iterator。 PHP docs

也有一个很好的例子

答案 5 :(得分:0)

Iterator接口为我提供了更易读的代码。如果您需要更具体的业务逻辑方法,那么您可以实现而不是按照您的意愿执行。或者直接创建快速的空类或实例。主要原因是干净的代码。

答案 6 :(得分:0)

从根本上来说,这涉及到您要开发的内容以及其他人是否会使用您的类来开发某些东西。

Iterator的简单形式在单级控制循环中的作用与IteratorAggregate一样,并且大多数是可互换的。

foreach ($it as $key => $value) {
    // Both examples from the original question will out put the same results.
}

Iterator失败的地方是高级逻辑,例如,当存在多个嵌套循环时。除非您在每个循环中手动将其重置,否则仅具有单个索引的简单迭代器将仅对最深层的控制循环循环一次。

$i = $j = 0;
foreach ($it as $key => $value) {
    foreach ($it as $key2 => $value2) {
        // Internal index is reset and then incremented as expected.
        $j++;
    }
    // Loop ends after 1 iteration unless you reset internal position.
    # $it->mySetIteratorPosition($i);
    $i++;
}
$i === 1;
$j === count($it);

迭代器没有函数来告知循环何时中断,因此,如果有人在完成循环之前退出了其中一个嵌套循环,则会轻易导致无限循环。

foreach ($it as $key => $value) {
    foreach ($it as $key2 => $value2) {
        // Internal pointer is reset back to 0
        break;
    }
    // Internal pointer is incremented from 0 to 1
}
// Unreachable statement

IteratorAggregate工作的地方是每当新循环开始时它就会创建一个新的Iterator对象。这意味着嵌套循环更易于维护,并且在循环结束时(或之后不久),垃圾收集器将丢弃新的Iterator对象。

如果您想手动控制rewindcurrentnextkeyvalid的处理,那么您要做的就是在Iterator中使用自定义getIterator类,或者扩展OuterIterator

class myData implements IteratorAggregate
{
    public function getIterator()
    {
        $it = new myIterator($this);
        $it->mySetArray((array) $this);
        return $it;
    }
}