【问题标题】:Difference between ArrayIterator, ArrayObject and Array in PHPPHP中ArrayIterator、ArrayObject和Array的区别
【发布时间】:2012-05-17 04:17:55
【问题描述】:

有人能解释清楚PHP中ArrayIteratorArrayObjectArray在功能和操作方面的根本区别吗?谢谢!

【问题讨论】:

标签: php arrays arrayobject arrayiterator


【解决方案1】:

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 ....

数组(作为本机类型)比对象快得多。另一方面,ArrayObjectArrayIterator 类定义了某些可以使用的方法,而数组则没有这样的东西


比较 ArrayObjectArrayIterator

这两者的主要区别在于类的方法。

ArrayIterator 实现了Iterator 接口,该接口为其提供了与元素迭代/循环相关的方法。 ArrayObject 有一个名为 exchangeArray 的方法,可以将其内部数组与另一个交换。在ArrayIterator 中实现类似的东西意味着要么创建一个新对象,要么循环遍历键和unset将它们一个一个一个一个地设置,然后一个一个地设置新数组中的元素。

接下来,由于ArrayObject 不能被迭代,当你在foreach 中使用它时,它会在内部创建一个ArrayIterator 对象(与数组相同)。这意味着 php 创建了原始数据的副本,现在有 2 个具有相同内容的对象。这对于大型阵列将被证明是低效的。但是,您可以指定将哪个类用于迭代器,因此您可以在代码中使用自定义迭代器。

【讨论】:

  • 关于arrayIterator的解释都是错误的。阅读我的答案。
  • @David 发布的信息有什么问题?
【解决方案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 并且本身就是(外部)迭代器。
【解决方案3】:

array 是 PHP 中的八种基本类型之一。尽管它带有很多内置的实用功能,但它们都是程序化的。

ArrayObjectArrayIterator 都允许我们在面向对象程序 (OOP) 中使数组成为一等公民。

ArrayObjectArrayIterator 的区别在于,由于ArrayIterator 实现了SeekableIterator 接口,你可以用ArrayIterator$myArray-&gt;seek(10);

【讨论】:

    【解决方案4】:

    Iterator 是一个对象,它使程序员能够遍历容器,尤其是列表。各种类型的迭代器通常通过容器的接口提供。

    ArrayObjectArray 之间没有太大区别,因为它们代表相同的事物,尽管使用不同的对象类型。

    ArrayIterator 是一个迭代 Array-like 对象的迭代器,这包括所有实现 ArrayAcess 和本机 Array 类型的对象。 事实上,当你 foreach 遍历一个数组时,PHP 会在内部创建 ArrayIterator 来进行遍历并将你的代码转换为看起来像是输入了这个,

    for( $arrayIterator->rewind(); $arrayIterator->valid(); $arrayIterator-
    >next())
    {    $key = $arrayIteartor->key();
         $value = $arrayIterator->current();
    }
    

    所以你可以看到,每个集合对象都有一个迭代器,除了你定义的集合,你需要定义你自己的迭代器。

    【讨论】:

      【解决方案5】:

      编辑:忘记在我的答案中处理普通数组..

      在您阅读所有这些之前,如果您是 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,我说这是要采用的逻辑路径。

      希望这对您有所帮助,就像它帮助我研究它一样。

      【讨论】:

      • 对不起,我忘记处理标准数组了....棘手的东西,然后只需使用标准数组。数组是它开始的地方,您需要先习惯使用它们,然后才能理解其余部分。
      【解决方案6】:

      PHP 中的数组实际上是一个有序映射。地图是一种类型 将值与键相关联。这种类型针对几个进行了优化 不同的用途;它可以被视为一个数组、列表(向量)、哈希 表(地图的实现)、字典、集合、堆栈、 排队,可能还有更多。由于数组值可以是其他数组,因此树 和多维数组也是可能的。

      这个类允许对象作为数组工作。

      此迭代器允许取消设置和修改值和键,同时 遍历数组和对象。

      当您想多次迭代同一个数组时,您需要 实例化 ArrayObject 并让它创建 ArrayIterator 实例 通过使用 foreach 或调用它来引用它 手动 getIterator() 方法。

      【讨论】:

      • 对不起,我没有把我的问题说清楚 - 是的,我已经阅读了这三个方面的 PHP 文档,但在我看来,他们做的事情几乎相同。我想知道是否有人可以解释它们之间的实际区别以及为什么您会选择其中一个。如果这不是一个适合在这里发布的问题,那么我很抱歉,请随时删除此帖子!
      【解决方案7】:

      Array 是 array 类型,ArrayObjectArrayIterator 是内置类,它们的实例是 object 类型,部分表现类似语法和使用级别的数组。

      对于类,我认为可以从它们实现的接口中获得快速的想法:

      class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable {
      
      class ArrayIterator implements SeekableIterator, ArrayAccess, Serializable, Countable {
      

      两个类都实现了:

      • ArrayAccess,所以它们支持数组语法来访问值
      • Countable,所以它们支持值的计数,就像数组一样
      • Iterator(虽然方式不同),所以它们适合 iterable 类型,并且可以用 foreach 等进行循环

      一种看待它的方式是,类在数组一样工作时遇到了很多麻烦。关键是作为类,它们可以扩展和定制。

      它们本质上也可以与通常期望 Iterator 的任何代码互操作,因此基本用例就是这样 - 包装以将数组数据提供给迭代器代码。

      简而言之,ArrayObjectArrayIteratorDo It Yourself 数组(两者之间存在一些实现差异)。它们的实例(部分)表现得像 array 类型,但作为类它们是可扩展的,作为 Iterator 实现者它们可以与需要的代码互操作。

      除非您需要深度自定义行为和/或Iterator 互操作性,否则坚持使用array 类型可能是介于两者之间的方法。

      值得注意的是,还有集合的实现,旨在通过更友好的抽象来提供类的好处。

      【讨论】:

        猜你喜欢
        • 2013-07-07
        • 1970-01-01
        • 1970-01-01
        • 2010-10-27
        • 2017-10-16
        • 2013-09-10
        • 1970-01-01
        • 2018-08-30
        • 2017-03-12
        相关资源
        最近更新 更多