web-dev-qa-db-ja.com

PHPのArrayIterator、ArrayObject、Arrayの違い

誰かがArrayIteratorArrayObjectArrayの基本的な違いをPHPの機能と操作の点で明確に説明できますか?ありがとう!

28
jkhamler

Arrayはネイティブのphpタイプです。 php言語コンストラクトarray()を使用して作成するか、php5.4以降[]を使用して作成できます。

ArrayObjectobjectであり、配列とまったく同じように機能します。これらはnewキーワードを使用して作成できます

ArrayIteratorArrayObjectに似ていますが、それ自体で反復できます。 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クラスには、使用できる特定のメソッドが定義されていますが、配列にはそのようなものはありません。


ArrayObjectArrayIteratorの比較

これら2つの主な違いは、クラスのメソッドにあります。

ArrayIteratorIteratorインターフェースを実装し、要素の反復/ループに関連するメソッドを提供します。 ArrayObjectには、内部配列を別の配列と交換するexchangeArrayというメソッドがあります。同様のことをArrayIteratorに実装するということは、新しいオブジェクトを作成するか、キーをループしてすべてを1つずつunsetしてから、新しい配列の要素を1つずつ設定することを意味します。

次に、ArrayObjectは反復できないため、foreachで使用すると、内部でArrayIteratorオブジェクトが作成されます(配列と同じ)。これは、phpが元のデータのコピーを作成し、同じ内容の2つのオブジェクトがあることを意味します。これは、大きなアレイでは非効率的であることがわかります。ただし、イテレータに使用するクラスを指定できるため、コードにカスタムイテレータを含めることができます。


これがお役に立てば幸いです。この回答の編集は大歓迎です。

26
Mridul Aggarwal

ArrayObjectとarrayは多少似ています。単なるオブジェクト(またはネイティブタイプ)のコレクション。それらには、呼び出すことができるいくつかの異なるメソッドがありますが、ほとんどの場合、同じものに要約されます。

ただし、イテレータはまったく別のものです。イテレータデザインパターンは、配列を保護する方法です(読み取り可能にするだけです)。次の例を見てみましょう:

配列を持つクラスがあります。 addSomethingToMyArrayを使用して、その配列にアイテムを追加できます。ただし、実際に配列に追加する前に、アイテムに対して何かを行うことに注意してください。これは何でもかまいませんが、配列に追加するすべてのアイテムに対してこのメ​​ソッドを実行することが非常に重要であるように、しばらくの間動作させます。

class A
{
    private $myArray;
    public function returnMyArray()
    {
        return $this->myArray;
    }

    public function addSomethingToMyArray( $item )
    {
        $this->doSomethingToItem( $item ); 
        array_Push( $item );
    }
}

これに伴う問題は、ここで参照によって配列を渡すことです。つまり、実際にreturnMyArrayを使用するクラスは、実際のmyArrayオブジェクトを取得します。つまり、A以外のクラスはその配列に何かを追加できるため、addSOmethingToMyArrayを使用せずにA内の配列も変更できます。しかし、doSOmethingToItemが必要でした、覚えていますか?これは、自身の内部ステータスを制御していないクラスの例です。

これに対する解決策はイテレータです。配列を渡す代わりに、配列を新しいオブジェクトに渡します。このオブジェクトは、配列からのものを読み取ることしかできません。これまでで最も単純なイテレータは次のようなものです。

<?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にアイテムを追加する方法は1つしかありません。つまり、addSomethingToArrayメソッドを使用します。これがイテレータであり、カプセル化と呼ばれるものを提供するための、配列の周りのシェルのようなものです。

10
David

編集:私の答えの通常の配列に対処するのを忘れました。

これをすべて読む前に、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 =概念の優れた例(低レベルのクラスのドキュメント)。

Intellisense suggestions of the arrow functions

ArrayObjectは、同じ方法で繰り返して値にアクセスできますが、ポインター関数へのパブリックアクセスは提供しません。

Intellisense of array object

オブジェクトは、ArrayItteratorオブジェクトの周りにラッパーを追加するようなもので、内部の値のトラバースを容易にするpublic getIterator ( void ) : ArrayIteratorを備えています。

追加された関数が本当に必要な場合は、いつでもArrayObjectからArrayItteratorを取得できます。

ArrayItteratorは、奇妙な方法でトラバースするための独自の疑似foreachループがあり、トラバーサルを開始して終了するのではなく、より適切に制御したい場合に最適です。

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

1つの例として、同じデータセットをトラバースするときに異なる形式で値を返すカスタムイテレータの束など、同じデータセットの出力を変更する場合があります。例えば...

_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

ArrayObject-データ用の1つのコンテナですが、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を利用できることを考えると、これが論理的なパスであるとidは言います。

これが私がそれを調べるのを助けたのと同じくらいあなたに役立つことを願っています。

0
Brad

Iterator は、プログラマーがコンテナー、特にリストをトラバースできるようにするオブジェクトです。多くの場合、さまざまなタイプのイテレータがコンテナのインターフェイスを介して提供されます。

ArrayObjectArrayは、異なるオブジェクトタイプを使用していても同じものを表すため、大きな違いはありません。

ArrayIteratorは、Array-likeオブジェクトを反復処理するイテレータです。これには、ArrayAcessとネイティブのArray型を実装するすべてのオブジェクトが含まれます。実際、配列に対してforeachを実行すると、PHPは内部でArrayIteratorを作成してトラバースを実行し、コードをこれを入力したように変換します。

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

ご覧のとおり、独自のイテレータを定義する必要がある定義済みのコレクションを除いて、すべてのコレクションオブジェクトにはイテレータがあります。

arrayは、PHPの8つのプリミティブ型の1つです。多くの組み込みユーティリティ関数が付属していますが、それらはすべて手続き型です。

ArrayObjectArrayIteratorの両方を使用すると、オブジェクト指向プログラム(OOP)で配列を第一級市民にすることができます。

ArrayObjectArrayIteratorの違いは、ArrayIteratorSeekableIteratorインターフェースを実装しているため、ArrayIterator$myArray->seek(10);を実行できることです。 ] _。

0
XuDing

ここであなたの答えが見つかるかもしれません:

このイテレータを使用すると、配列とオブジェクトを反復処理しながら、値とキーを設定解除および変更できます。

同じ配列を複数回反復する場合は、ArrayObjectをインスタンス化し、foreachを使用するか、getIterator()メソッドを手動で呼び出すことにより、それを参照するArrayIteratorインスタンスを作成する必要があります。

また読む:

0
Naveed