foreach を使用して PHP 配列にアクセスする場合、トラバーサルの順序は固定されていますか?
例:
コードは次のとおりです:
$ arr ['ラルエンス'] = 'フイシンチェン';
$arr['yahoo'] = 2007;
$arr['baidu'] = 2008; ($arr as $key => $val) {
/ /結果はどうなりますか?
}
$arr[2] = 'huixinchen' = 2007 ;
$ arr[0] = 2008;
foreach ($arr as $key => $val) {//結果はどうなりましたか?
この問題を完全に理解するには、次のようにする必要があると思います。まず皆さんに聞いてください PHP 配列の内部実装構造を理解してください...
PHP 配列
PHP では、配列は HASH 構造 (HashTable) を使用して実装されており、PHP は O(1) の時間計算量でそれを可能にするいくつかのメカニズムを使用しています。配列の追加と削除を高速に実行し、線形トラバーサルとランダムアクセスを同時にサポートします
これに基づいて、さらに拡張を行います
。 HashTable を知るために、まず見てみましょう HashTable の構造定義を見て、誰でも理解できるようにコメントを追加しました。 コードは次のとおりです。
typedef struct _hashtable {
uint nTableSize;テーブル サイズ、ハッシュ値の範囲*/
uint nTableMask; /* nTableSize -1 に等しい、簡単な位置決めに使用*/
uint nNumOfElements; /* HashTable 内の実際の要素の数*/
ulong nNextFreeElement;次に使用可能な空き位置の数値インデックス*/
Bucket *pInternalPointer; /* 内部位置ポインターはリセットされ、現在の値になります。 */
Bucket *pListHead /* 線形走査に使用されるヘッド要素。 Bucket *pListTail; /* 線形走査に使用される末尾要素 */
zend_boolpersistent; /* ループトラバーサル保護 */
zend_bool bApplyProtection;#if ZEND_DEBUG
#endif
} HashTable;
の例で理解できます。コード
$arr = array(1, 2,3,4,5,);
var_export($arr);エラー: ネストレベルが深すぎます - 再帰的な依存関係ですか?
このフィールドは、循環参照によって引き起こされる無限ループを防ぐために確立されています
上記の構造を見ると、HashTable の重要な要素が次のとおりであることがわかります。実際のストレージ コンテナーである arBuckets の構造定義を見てみましょう:
typedef structbucket { /* 数値インデックス/ハッシュ値*/
uint nKeyLength; /* 文字インデックスの長さ*/
struct Bucket *pListNext; /* 線形トラバーサルに使用されます。 structbucket *pListLast; /* 線形走査に使用される前の要素 */
structbucket *pNext; / * 同じジッパー内の次の要素 */struct Bucket *pLast;
char arKey[1]; /* メモリを節約し、初期化を容易にするためのヒント*/
最後の要素は、メモリを節約し、初期化を容易にする柔軟な配列手法であることに気付きました。興味のある方は、柔軟な配列をグーグルで検索してください。要素のハッシュ値は、数値インデックスです。は直接のインデックス値 (nKeyLength=0 で表される数値インデックス) です。文字列インデックスの場合、インデックス値は arKey に格納され、インデックスの長さは nKeyLength に格納されます。 pData ポインタが指すメモリ ブロック。通常、このメモリ ブロックはシステムによって個別に割り当てられます。ただし、例外があります。つまり、バケットによって保存されたデータがポインターの場合、HashTable はシステムにポインターを保存するためのスペースの割り当てを要求せず、ポインターを pDataPtr に直接保存し、pData をメンバーにポイントします。この構造体のアドレス。これにより効率が向上し、メモリの断片化が軽減されます。このことから、PHP HashTable の設計の微妙な点がわかります。 Bucket 内のデータがポインターでない場合、pDataPtr は NULL になります (この段落は Altair の「Zend HashTable 詳細説明」から引用しています)
HashTable の pListhHead は、線形リスト形式の最初の要素を指します。は要素 1、pListTail は を指します。最後の要素は 0 で、各要素の pListNext は赤い線で描かれた線形構造の次の要素、pListLast は現在の要素の位置を指します。内部ポインター。配列を順次走査する場合、このポインターは現在の要素を示します。線形 (逐次) 走査が実行される場合、pListHead から開始し、Bucket 内の pListNext/pListLast に従い、すべての要素の線形走査を達成するために pInternalPointer を移動します。
たとえば、foreach の場合、生成されるオペコード シーケンスを見ると、foreach の前に、配列の内部ポインター (pInternalPointer) をリセットするための FE_RESET があることがわかります (foreach の場合は、 「PHP 原理の詳細な理解: foreach )」を参照して、各 FE_FETCH を通じて pInternalPointer をインクリメントして、逐次走査を実現します
同様に、一連の関数 each/next を使用して走査する場合も、内部関数を移動することで逐次走査を実現します。配列のポインター。例:
。コードは次のとおりです。
$arr = array(1,2,3,4,5); $arr as $v) {
//OK
を取得します}
//
を取得できません}?>
理解foreach は自動的にリセットされますが、while ブロックはリセットされないため、foreach が終了した後、pInternalPointer は配列の末尾を指し、もちろん while ステートメント ブロックはできません。解決策は、それぞれのアクセスの前に配列の内部ポインターをリセットすることです。
ランダムアクセス中、ハッシュ配列内の先頭ポインターの位置は、pNext を通じて検出されます。 /pLast.
要素を追加する場合、要素は同じハッシュ要素チェーンの先頭と線形リストの末尾に挿入されます。つまり、要素は線形走査中の挿入順序に従って走査されます。この特別な設計により、PHP で数値インデックスを使用する場合、要素の順序はインデックスの順序ではなく加算の順序によって決定されます
つまり、PHP で配列を走査する順序は順序に関係します。これで、記事の冒頭が問題の出力であることが明確に分かりました:
コードは次のとおりです:
数値インデックス付き配列のインデックス サイズに従って反復するには、foreach
ではなく for を使用する必要があります。 コードは次のとおりです。
for($i=0,$l=count($arr); $i<$l; $i++) {
//現時点では、逐次走査 (線形走査) とはみなされません}
上記は PHP 配列 (走査順序) の詳細な理解です。関連記事については、PHP 中国語 Web サイト (www.php.cn) にご注意ください。