23

IteratorAggregate是一个创建外部 Iterator 的接口

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

WhileIterator外部迭代器或可以在内部迭代的对象的接口

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

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

4

7 回答 7

24

我假设IteratorAggregate适用于您想要节省时间Iterator的情况,并且适用于您需要通过对象对迭代进行精细控制的情况。例如,它允许您添加自定义异常或故障next()、缓存(如果您遍历通过网络获取数据的东西)、在返回值之前预处理值。key()prev()

于 2012-11-29T11:32:07.507 回答
12

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

如果你需要实现自定义方法,你需要实现一个OuterIteratoror (更容易)来扩展一个IteratorIterator.

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

这是我在我可怜的笔记本电脑上运行的一个简单基准测试,每个 100 万次迭代IteratorAggregateIteratorIterator并且OuterIterator都使用iterator_to_arrayforeach: (还要注意echo内部聚合的next方法:没有打印'x',证明它从未被调用)

$ 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);
}
于 2014-11-19T20:24:45.730 回答
7

他们之间有什么区别?

名称的差异(“聚合”)。

聚合是什么意思?

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


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

对比

0: one
    0: one
    1: two
    2: three
于 2016-11-17T09:37:15.473 回答
6

通过实现IteratorAggregate,我们将实现迭代器函数(key(), next(), current(), valid(), rewind())的工作委托给其他类(通过仅实现getIterator().

这样,它可以帮助我们在 OOP 中实现关注点分离。

于 2016-12-27T14:04:53.767 回答
2

首先,这一切都取决于您要开发什么,以及其他人是否会使用您的类来开发某些东西。

简单的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当循环结束时(或不久之后)垃圾收集器会丢弃新对象。

如果您想手动控制、、或的处理rewind,那么您需要做的就是在 中使用自定义类,或者扩展。currentnextkeyvalidIteratorgetIteratorOuterIterator

class myData implements IteratorAggregate
{
    public function getIterator()
    {
        $it = new myIterator($this);
        $it->mySetArray((array) $this);
        return $it;
    }
}
于 2019-04-08T08:03:14.523 回答
2

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

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

如果一个类实现了,您可以在foreach 循环Traversable中调用它并对其进行迭代。但是,一个类不能直接实现,因为它是一个抽象基接口。Traversable

相反,一个类可以实现IteratorAggregateIterator. 所以它们都有一个共同点,如果你想让一个对象可以用 a 调用,foreach loop它需要实现其中一个(或它们的任何子级)。

Iterator如果一个人想要控制指针并给出how它应该迭代的精确指令,一个实现。如果一个人想要Iterator使用在foreach loop一个工具中使用的现有IteratorAggregate

而且您始终可以使用IteratorIteratorIteratorAggregate将 a 转换为Iteratora 。PHP文档中也有一个很好的例子

于 2020-05-17T10:50:28.950 回答
0

对我来说,迭代器接口使代码更具可读性。如果您的业务逻辑需要更具体的方法,那么您可以实现而不是做您想做的事情。或者直接快速创建空类或实例。主要原因是干净的代码。

于 2018-05-23T14:39:27.277 回答