【发布时间】:2012-05-17 04:17:55
【问题描述】:
有人能解释清楚PHP中ArrayIterator、ArrayObject和Array在功能和操作方面的根本区别吗?谢谢!
【问题讨论】:
-
你读过ArrayIterator、ArrayObject和Array吗?这可能就是您需要的所有信息。
标签: php arrays arrayobject arrayiterator
有人能解释清楚PHP中ArrayIterator、ArrayObject和Array在功能和操作方面的根本区别吗?谢谢!
【问题讨论】:
标签: php arrays arrayobject arrayiterator
Array 是原生 php 类型。您可以使用 php 语言构造 array() 或从 php 5.4 开始创建一个 []
ArrayObject 是一个object,其工作方式与数组完全相同。这些可以使用new 关键字创建
ArrayIterator 类似于ArrayObject,但它可以自行迭代。也是使用new创建的
比较 Array 与 (ArrayObject/ArrayIterator)
它们都可以使用 php 的数组语法,例如。
$array[] = 'foo';
$object[] = 'foo';
// adds new element with the expected numeric key
$array['bar'] = 'foo';
$object['bar'] = 'foo';
// adds new element with the key "bar"
foreach($array as $value);
foreach($object as $value);
// iterating over the elements
但是,它们仍然是对象与数组,因此您会注意到
is_array($array); // true
is_array($object); // false
is_object($array); // false
is_object($object); // true
大多数 php 数组函数都需要数组,因此在其中使用对象会引发错误。有很多这样的功能。例如。
sort($array); // works as expected
sort($object); // Warning: sort() expects parameter 1 to be array, object given in ......
最后,对象可以执行您对 stdClass 对象的期望,即使用对象语法访问公共属性
$object->foo = 'bar'; // works
$array->foo = 'bar'; // Warning: Attempt to assign property of non-object in ....
数组(作为本机类型)比对象快得多。另一方面,ArrayObject 和 ArrayIterator 类定义了某些可以使用的方法,而数组则没有这样的东西
比较 ArrayObject 与 ArrayIterator
这两者的主要区别在于类的方法。
ArrayIterator 实现了Iterator 接口,该接口为其提供了与元素迭代/循环相关的方法。 ArrayObject 有一个名为 exchangeArray 的方法,可以将其内部数组与另一个交换。在ArrayIterator 中实现类似的东西意味着要么创建一个新对象,要么循环遍历键和unset将它们一个一个一个一个地设置,然后一个一个地设置新数组中的元素。
接下来,由于ArrayObject 不能被迭代,当你在foreach 中使用它时,它会在内部创建一个ArrayIterator 对象(与数组相同)。这意味着 php 创建了原始数据的副本,现在有 2 个具有相同内容的对象。这对于大型阵列将被证明是低效的。但是,您可以指定将哪个类用于迭代器,因此您可以在代码中使用自定义迭代器。
【讨论】:
ArrayObject 和数组有些相似。仅仅是对象(或本机类型)的集合。它们有一些你可以调用的不同方法,但大部分都归结为同一件事。
然而,迭代器完全是另外一回事。迭代器设计模式是一种保护数组(使其仅可读)的方法。让我们看下一个例子:
你有一个有数组的类。您可以使用 addSomethingToMyArray 将项目添加到该数组。但是请注意,我们在将 item 实际添加到数组之前对其进行了一些操作。这可以是任何东西,但让我们暂时假设为我们要添加到数组中的每个项目触发此方法非常重要。
class A
{
private $myArray;
public function returnMyArray()
{
return $this->myArray;
}
public function addSomethingToMyArray( $item )
{
$this->doSomethingToItem( $item );
array_push( $item );
}
}
这个问题是你在这里通过引用传递数组。这意味着实际使用 returnMyArray 的类会获得真正的 myArray 对象。这意味着除了 A 之外的类可以向该数组添加内容,因此也可以更改 A 内的数组,而无需使用 addSOmethingToMyArray。但是我们需要做一些事情,记得吗?这是一个无法控制其自身内部状态的类的示例。
解决方案是迭代器。我们没有传递数组,而是将数组传递给一个新对象,该对象只能从数组中读取内容。最简单的迭代器是这样的:
<?php
class MyIterator{
private $array;
private $index;
public function __construct( $array )
{
$this->array = $array;
}
public function hasNext()
{
return count( $this->array ) > $this->index;
}
public function next()
{
$item = $this->array[ $this->index ];
this->$index++;
return $item;
}
}
?>
如您所见,我无法向给定数组添加新项目,但我确实可以像这样读取数组:
while( $iterator->hasNext() )
$item = $iterator->next();
现在又只有一种方法可以将项目添加到 A 中的 myArray,即通过 addSomethingToArray 方法。这就是迭代器,它有点像数组周围的外壳,提供称为封装的东西。
【讨论】:
ArrayObject 实现 IteratorAggregate 并且本身就是(外部)迭代器。
array 是 PHP 中的八种基本类型之一。尽管它带有很多内置的实用功能,但它们都是程序化的。
ArrayObject 和ArrayIterator 都允许我们在面向对象程序 (OOP) 中使数组成为一等公民。
ArrayObject 和ArrayIterator 的区别在于,由于ArrayIterator 实现了SeekableIterator 接口,你可以用ArrayIterator 做$myArray->seek(10);。
【讨论】:
Iterator 是一个对象,它使程序员能够遍历容器,尤其是列表。各种类型的迭代器通常通过容器的接口提供。
ArrayObject 和 Array 之间没有太大区别,因为它们代表相同的事物,尽管使用不同的对象类型。
ArrayIterator 是一个迭代 Array-like 对象的迭代器,这包括所有实现 ArrayAcess 和本机 Array 类型的对象。
事实上,当你 foreach 遍历一个数组时,PHP 会在内部创建 ArrayIterator 来进行遍历并将你的代码转换为看起来像是输入了这个,
for( $arrayIterator->rewind(); $arrayIterator->valid(); $arrayIterator-
>next())
{ $key = $arrayIteartor->key();
$value = $arrayIterator->current();
}
所以你可以看到,每个集合对象都有一个迭代器,除了你定义的集合,你需要定义你自己的迭代器。
【讨论】:
编辑:忘记在我的答案中处理普通数组..
在您阅读所有这些之前,如果您是 php 新手,并且不想做任何棘手的事情,只是存储值以供以后使用,那么只需使用数组原始类型。
$data = [1,2,3,4,5];
//or
$data = array(1,2,3,4,5);
数组是入口点,你应该先习惯使用它们,然后再考虑使用其他的。如果您不了解数组,那么您可能不会了解使用其他数组的好处。 (有关数组的教程,请参阅https://www.w3schools.com/php/php_arrays.asp。)
否则继续阅读...
我有同样的问题,来到这里后决定花一些时间测试一下,这是我的发现和结论......
首先让我们澄清一下我立即测试的其他人所说的一些内容。
ArrayObject 和 ArrayItterator 不保护数据。两者仍然可以在 for-each 循环中通过引用传递(例如,请参见底部)。
is_object() 都返回 true,is_array() 都返回 false,并且都允许以数组的形式直接访问值,而不提供对添加值等的保护,并且两者都可以通过引用传递,从而允许原始数据是在 foreach 循环期间进行操作。
$array = new ArrayIterator();
var_dump(is_array($array)); // false
var_dump(is_object($array)); // true
$array[] = 'value one';
var_dump($array[0]);//string(9) "value one"
$array = new ArrayObject();
var_dump(is_array($array)); // false
var_dump(is_object($array)); // true
$array[] = 'value one';
var_dump($array[0]);//string(9) "value one"
可以在它们中的任何一个可用的功能中看到最大的区别。
ArrayIteroator 具有遍历值所需的所有功能,例如 foreach 循环。 (foreach 循环将调用 rewind(),valid(),current(),key() 方法) 请参阅:https://www.php.net/manual/en/class.iterator.php 以获得该概念的一个很好的示例(较低级别的类文档)。
虽然 ArrayObject 仍然可以迭代并以相同的方式访问值,但它不提供对指针函数的公共访问。
Object 有点像在 ArrayItterator 对象周围添加一个包装器,并具有public getIterator ( void ) : ArrayIterator,这将有助于遍历内部的值。
如果您确实需要添加的功能,您可以随时从 ArrayObject 获取 ArrayItterator。
如果您有自己的伪 foreach 循环以奇怪的方式遍历并且想要更好的控制而不是仅仅开始到结束遍历,那么 ArrayItterator 是最佳选择。
ArrayItterator 也是一个很好的选择,用于在通过 foreach 循环进行迭代时覆盖默认数组行为。比如……
//I originally made to this to solve some problem where generic conversion to XML via a foreach loop was used,
//and I had a special case where a particular API wanted each value to have the same name in the XML.
class SameKey extends ArrayIterator{
public function key()
{
return "AlwaysTheSameKey";
}
}
$extendedArrayIterator = new SameKey(['value one','value two','value three']);
$extendedArrayIterator[] = 'another item added after construct';
//according to foreach there all the keys are the same
foreach ($extendedArrayIterator as $key => $value){
echo "$key: ";//key is always the same
var_dump($value);
}
//can still be access as array with index's if you need to differentiate the values
for ($i = 0; $i < count($extendedArrayIterator); $i++){
echo "Index [$i]: ";
var_dump($extendedArrayIterator[$i]);
}
如果你有更高层次的复杂性,例如迭代器,ArrayObject 可能是一个不错的选择......
//lets pretend I have many custom classes extending ArrayIterator each with a different behavior..
$O = new ArrayObject(['val1','val2','val3']);
//and I want to change the behavior on the fly dynamically by changing the iterator class
if ($some_condition = true) $O->setIteratorClass(SameKey::class);
foreach ($O as $key => $value){
echo "$key: ";//AlwaysTheSameKey:
var_dump($value);
}
一个示例可能是更改同一数据集的输出,例如具有一组自定义迭代器,这些迭代器在遍历同一数据集时将以不同格式返回值。比如……
class AustralianDates extends ArrayIterator{
public function current()
{
return Date('d/m/Y',parent::current());
}
}
$O = new ArrayObject([time(),time()+37474,time()+37845678]);
//and I want to change the behaviour on the fly dynamically by changing the iterator class
if ($some_condition = true) $O->setIteratorClass(AustralianDates::class);
foreach ($O as $key => $value){
echo "$key: ";//AlwaysTheSameKey:
var_dump($value);
}
显然可能有更好的方法来做这种事情。
简而言之,这些是我可以看到的主要主要优势差异。
ArrayItorator - 控制或扩展和覆盖遍历行为的较低级别的能力。
VS
数组对象 - 一个数据容器,但可以更改 ArrayIterator 类。
您可能还想检查其他差异,但我想您在广泛使用它们之前不会完全掌握它们。
似乎这两个对象都可以在 foreach 中通过引用来使用,但在通过 ArrayObject 使用自定义迭代器类时则不能..
//reference test
$I = new ArrayIterator(['mouse','tree','bike']);
foreach ($I as $key => &$value){
$value = 'dog';
}
var_dump($I);//all values in the original are now 'dog'
$O = new ArrayObject(['mouse','tree','bike']);
foreach ($O as $key => &$value){
$value = 'dog';
}
var_dump($O);//all values in the original are now 'dog'
$O->setIteratorClass(SameKey::class);
foreach ($O as $key => &$value){//PHP Fatal error: An iterator cannot be used with foreach by reference
$value = 'dog';
}
var_dump($O);
建议/结论
使用数组。
如果你想做一些棘手的事情, 我建议始终使用 ArrayIterator 开始,如果你想要特定的东西,只有 ArrayObject 可以做,才继续使用 ArrayObject。
考虑到 ArrayObject 可以使用您在此过程中创建的任何自定义 ArrayIterator,我说这是要采用的逻辑路径。
希望这对您有所帮助,就像它帮助我研究它一样。
【讨论】:
PHP 中的数组实际上是一个有序映射。地图是一种类型 将值与键相关联。这种类型针对几个进行了优化 不同的用途;它可以被视为一个数组、列表(向量)、哈希 表(地图的实现)、字典、集合、堆栈、 排队,可能还有更多。由于数组值可以是其他数组,因此树 和多维数组也是可能的。
这个类允许对象作为数组工作。
此迭代器允许取消设置和修改值和键,同时 遍历数组和对象。
当您想多次迭代同一个数组时,您需要 实例化 ArrayObject 并让它创建 ArrayIterator 实例 通过使用 foreach 或调用它来引用它 手动 getIterator() 方法。
【讨论】:
Array 是 array 类型,ArrayObject 和 ArrayIterator 是内置类,它们的实例是 object 类型,部分表现类似语法和使用级别的数组。
对于类,我认为可以从它们实现的接口中获得快速的想法:
class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable {
class ArrayIterator implements SeekableIterator, ArrayAccess, Serializable, Countable {
两个类都实现了:
ArrayAccess,所以它们支持数组语法来访问值Countable,所以它们支持值的计数,就像数组一样Iterator(虽然方式不同),所以它们适合 iterable 类型,并且可以用 foreach 等进行循环一种看待它的方式是,类在像数组一样工作时遇到了很多麻烦。关键是作为类,它们可以扩展和定制。
它们本质上也可以与通常期望 Iterator 的任何代码互操作,因此基本用例就是这样 - 包装以将数组数据提供给迭代器代码。
简而言之,ArrayObject 和 ArrayIterator 是 Do It Yourself 数组(两者之间存在一些实现差异)。它们的实例(部分)表现得像 array 类型,但作为类它们是可扩展的,作为 Iterator 实现者它们可以与需要的代码互操作。
除非您需要深度自定义行为和/或Iterator 互操作性,否则坚持使用array 类型可能是介于两者之间的方法。
值得注意的是,还有集合的实现,旨在通过更友好的抽象来提供类的好处。
【讨论】: