IteratorAggregate
là cách dễ dàng để triển khai Iterator. Điểm bất lợi là bạn không thể thêm next()
, key()
, vv các phương thức, vì chúng sẽ không được gọi trong quá trình truyền tải thông thường foreach
.
Nếu bạn cần triển khai các phương thức tùy chỉnh, bạn cần triển khai OuterIterator
hoặc (dễ dàng hơn) để mở rộng IteratorIterator
.
Lợi thế với IteratorAggregate
là tốc độ, tốc độ này nhanh hơn nhiều so với các lựa chọn thay thế của nó. Vấn đề với nó là ... mặc dù tên nó không phải là một iterator nhưng một traversable (như vậy một lần nữa, không có tiếp theo, chính, hiện tại, hợp lệ, phương pháp tua lại).
Dưới đây là một chuẩn mực đơn giản, tôi chạy trên máy tính xách tay nghèo của tôi với 1 triệu lần lặp lại cho mỗi người IteratorAggregate
, IteratorIterator
và OuterIterator
cả sử dụng iterator_to_array
và foreach
: (cũng lưu ý các echo
bên trong phương pháp next
tổng hợp của: không có 'x' được in, giấy tờ chứng minh nó không bao giờ gọi)
$ 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
Dưới đây là đoạn code tôi sử dụng cho điểm chuẩn:
<?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);
}
Tôi nghĩ bạn sai về các phương pháp tùy chỉnh. – tacone
Và nếu bạn muốn tiết kiệm nhiều thời gian hơn, bạn có thể chỉ cần 'yield' các giá trị từ' getIterator'. – Bell