In-depth understanding of PHP array (traversal order) original Laruence

Source: Internet
Author: User
I am often asked if the PHP array is accessed using foreach, is the traversal order fixed? In what order does it traverse? As shown below, Laruence often asks me if the PHP array is accessed using foreach. is the traversal order fixed? In what order does it traverse?
For example:
The code is as follows:
$ Arr ['laruence '] = 'huixinchen ';
$ Arr ['Yahoo '] = 2007;
$ Arr ['baidu'] = 2008;
Foreach ($ arr as $ key => $ val ){
// What is the result?
}

For example:
The code is as follows:
$ Arr [2] = 'huixinchen ';
$ Arr [1] = 2007;
$ Arr [0] = 2008;
Foreach ($ arr as $ key => $ val ){
// What is the current result?
}

To fully understand this problem, I would like to first understand the internal implementation structure of the PHP array .........

PHP array
In PHP, arrays are implemented using a HASH structure (HashTable). PHP uses some mechanisms to add, delete, and delete arrays with time complexity of O (1, linear traversal and random access are also supported.

As discussed in previous articles, we have made further extensions based on the php hash algorithm.

Before learning about HashTable, let's take a look at the structure definition of HashTable. I added a comment to help you understand it:
The code is as follows:
Typedef struct _ hashtable {
Uint nTableSize;/* Hash size, Hash value range */
Uint nTableMask;/* is equal to nTableSize-1 for fast locating */
Uint nNumOfElements;/* Number of actual elements in HashTable */
Ulong nNextFreeElement;/* digital index of the next idle available location */
Bucket * pInternalPointer;/* internal position pointer, which will be reset and used by current traversal functions */
Bucket * pListHead;/* header element for linear traversal */
Bucket * pListTail;/* tail element for linear traversal */
Bucket ** arBuckets;/* Actual storage container */
Dtor_func_t pDestructor;/* destructor of the element (pointer )*/
Zend_bool persistent;
Unsigned char nApplyCount;/* loop traversal protection */
Zend_bool bApplyProtection;
# If ZEND_DEBUG
Int inconsistent;
# Endif
} HashTable;

For the meaning of nApplyCount, we can use an example to understand:
The code is as follows:
$ Arr = array (1, 2, 3, 4, 5 ,);
$ Arr [] = & $ arr;

Var_export ($ arr); // Fatal error: Nesting level too deep-recursive dependency?

This field is set up to prevent infinite loops caused by circular references.

Looking at the above structure, we can see that for HashTable, the key element is arBuckets. this is the actual storage container. let's take a look at its structure definition:
The code is as follows:
Typedef struct bucket {
Ulong h;/* numeric index/hash value */
Uint nKeyLength;/* length of the character Index */
Void * pData;/* Data */
Void * pDataPtr;/* data pointer */
Struct bucket * pListNext;/* next element for linear traversal */
Struct bucket * pListLast;/* The last element for linear traversal */
Struct bucket * pNext;/* next element in the same zipper */
Struct bucket * pLast;/* an element on the same zipper */
Char arKey [1];/* memory saving and convenient initialization tips */
} Bucket;


We have noticed that the last element is the flexible array technique, which can save memory and facilitate initialization. if you are interested, you can google flexible array.
H is the Hash value of an element. for a digital index element, h is the direct index value (represented by nKeyLength = 0 ). for string indexes, the index value is stored in arKey, and the index length is stored in nKeyLength.
In a Bucket, the actual data is stored in the memory block pointed to by the pData pointer. Generally, this memory block is allocated by the system separately. One exception is that when the data stored in the Bucket is a pointer, HashTable will not request the system to allocate space to save the pointer, but directly save the pointer to pDataPtr, then point pData to the address of the Member in this structure. This improves efficiency and reduces memory fragments. From this we can see the subtlety of the PHP HashTable design. If the data in the Bucket is not a pointer, pDataPtr is NULL (this section is from "Zend HashTable details" of Altair ")
In combination with the HashTable structure above, we will explain the HashTable's summary diagram:

HashTable's pListhHead points to the first element in the form of a linear list, which is element 1, and pListTail points to the last element 0, for each element, pListNext is the next element of the linear structure drawn in red lines, while pListLast is the previous element.

PInternalPointer points to the position of the current internal pointer. during sequential traversal of the array, this pointer specifies the current element.

When linear (sequential) traversal is performed, it starts from pListHead and follows pListNext/pListLast in the Bucket to implement linear traversal of all elements based on moving pInternalPointer.

For example, for foreach, if we look at the opcode sequence generated by it, we can find that before foreach, we will first have a FE_RESET to reset the internal pointer of the array, that is, pInternalPointer (for foreach, refer to foreach, which has a deep understanding of PHP principles), and then increment pInternalPointer through FE_FETCH each time to achieve sequential traversal.

Similarly, when we use functions of the each/next series to traverse, we also implement sequential traversal by moving the internal pointer of the array. here there is a problem, for example:
The code is as follows:
$ Arr = array (1, 2, 3, 4, 5 );
Foreach ($ arr as $ v ){
// Available
}

While (list ($ key, $ v) = each ($ arr )){
// Cannot be obtained
}
?>

After learning about the knowledge I just introduced, this problem will be very clear, because foreach will automatically reset, while this part will not reset, so after foreach ends, pInternalPointer points to the end of the array, the while statement block cannot be accessed. the solution is to reset the internal pointer of the array before each.

During random access, the position of the header pointer in the hash array is determined by the hash value, and the characteristic elements are found through pNext/pLast.

When an element is added, the element is inserted in the header of the same Hash element chain and the end of the linear list. that is to say, elements are traversed in the order of insertion in linear traversal. this special design makes it possible to use a digital index in PHP, the order of elements is determined by the order of addition, rather than the index order.

That is to say, the order of traversing arrays in PHP is related to the addition of elements. now we know clearly that the output of the problem at the beginning of the article is:
The code is as follows:
Huixinchen
2007
2008

Therefore, if you want to traverse the array of numeric indexes by index size, you should use for instead of foreach.
The code is as follows:
For ($ I = 0, $ l = count ($ arr); $ I <$ l; $ I ++ ){
// At this time, it cannot be considered sequential traversal (linear traversal)
}

Original article: http://www.laruence.com/2009/08/23/1065.html
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.