php Hash Table(四) Hash Table添加和更新元素
HashTable添加和更新的函數:
有4個主要的函數用于插入和更新HashTable的數據:
int zend_hash_add(HashTable *ht, char *arKey, uint nKeyLen,void **pData, uint nDataSize, void *pDest); int zend_hash_update(HashTable *ht, char *arKey, uint nKeyLen, void *pData, uint nDataSize, void **pDest);int zend_hash_index_update(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest);
int zend_hash_next_index_insert(HashTable *ht, void *pData, uint nDataSize, void **pDest);
這里的前兩個函數用于新增關聯索引數據, 比如$foo['bar'] = 'baz';對應的C語言代碼如下:
zend_hash_add(fooHashTbl, "bar", sizeof("bar"), &barZval, sizeof(zval*), NULL);zend_hash_add()和zend_hash_update()唯一的區別是如果key存在, zend_hash_add()將會失敗.
接下來的兩個函數以類似的方式處理數值索引的HashTable. 這兩行之間的區別在于是否指定索引 或者說是否自動賦值為下一個可用索引.
如果需要存儲使用zend_hash_next_index_insert()插入的元素的索引值, 可以調用zend_hash_next_free_element()函數獲得:
ulong nextid = zend_hash_next_free_element(ht); zend_hash_index_update(ht, nextid, &data, sizeof(data), NULL);?
HashTable添加更新元素:
在初始化了HashTable之后,可以用zend_hash_add來向HashTable添加元素 ,zend_hash_add是一個宏:
#define zend_hash_add(ht, arKey, nKeyLength, pData, nDataSize, pDest) \_zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_ADD ZEND_FILE_LINE_CC)?我們來看看_zend_hash_add_or_update的定義,同樣在Zend/zend_hash.c下
ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC) {ulong h; /*存貯arKey在hash之后的值*/uint nIndex; /*存貯h & nTableMask之后的值*/Bucket *p; #ifdef ZEND_SIGNALSTSRMLS_FETCH(); //這個還不知道是什么意思 #endifIS_CONSISTENT(ht); //調試信息輸出if (nKeyLength <= 0) { //添加的是字符串索引的,所以nKeyLength不可能<=0 #if ZEND_DEBUGZEND_PUTS("zend_hash_update: Can't put in empty key\n"); #endifreturn FAILURE;}/** * 檢查是否初始化buckets空間,若沒有初始化則初始化buckets的內存空間* 為arBuckets申請內存,為nTableSize賦值,因為在zend_hash_init里邊nTableSize設置為0 */CHECK_INIT(ht); h = zend_inline_hash_func(arKey, nKeyLength); /* 計算key的hash值 */nIndex = h & ht->nTableMask; /* 利用掩碼得到key的實際存儲位置 */p = ht->arBuckets[nIndex]; /* 取到指定位置的bucket指針 */while (p != NULL) { /* 若指針不為空,則表示當前位置已有bucket了 */if (p->arKey == arKey || ((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {/* 若當前bucket的key和要存入的key相同,那么需要更新 */if (flag & HASH_ADD) { /* 如果當前指定是add操作,此時就返回失敗了 */return FAILURE;}/*** interruptions,打斷,中斷的意思*/HANDLE_BLOCK_INTERRUPTIONS(); #if ZEND_DEBUGif (p->pData == pData) {ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pData\n");HANDLE_UNBLOCK_INTERRUPTIONS();return FAILURE;} #endifif (ht->pDestructor) { /* 調用析構函數析構掉原先的值 */ht->pDestructor(p->pData);}UPDATE_DATA(ht, p, pData, nDataSize); /* 替換為新的值 */if (pDest) {*pDest = p->pData;}HANDLE_UNBLOCK_INTERRUPTIONS();return SUCCESS;}p = p->pNext; /* 若當前key和要存入的key不同,那么查找Hash拉鏈的下一個bucket}/* 運行到這里,表示沒有找到任何已存在的key和要存入的key相同的,那么申請一個sizeof(bucket)+nKeyLength大小的新空間給key *///interned用google搜了一下,發現是'字符串駐留'的概念,也沒搞太清楚//大概就是維護了一個駐留池,會把在編譯期間相同的字符串只保留一份拷貝。if (IS_INTERNED(arKey)) { p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);if (!p) {return FAILURE;}p->arKey = arKey;} else {p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent); //柔性數組的概念if (!p) {return FAILURE;}p->arKey = (const char*)(p + 1); //p+1就是arKey的起始地址memcpy((char*)p->arKey, arKey, nKeyLength);}p->nKeyLength = nKeyLength;INIT_DATA(ht, p, pData, nDataSize); /* 執行賦值 */p->h = h;CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); /* 設置亂七八槽的指針 */if (pDest) {*pDest = p->pData;}HANDLE_BLOCK_INTERRUPTIONS();CONNECT_TO_GLOBAL_DLLIST(p, ht); /* 將Bucket 加入到HashTable的雙向鏈表中 */ht->arBuckets[nIndex] = p;HANDLE_UNBLOCK_INTERRUPTIONS();ht->nNumOfElements++;// 如果HashTable已滿,重新調整HashTable的大小。ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */return SUCCESS;上邊的函數中涉及到宏CHECK_INIT,在Zend_hash.c中定義如下,
#define CHECK_INIT(ht) do { \if (UNEXPECTED((ht)->nTableMask == 0)) { \(ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent); \(ht)->nTableMask = (ht)->nTableSize - 1; \} \ } while (0)INIT_DATA宏的定義,
#define INIT_DATA(ht, p, pData, nDataSize); \if (nDataSize == sizeof(void*)) { \memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \(p)->pData = &(p)->pDataPtr; \} else { \(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\if (!(p)->pData) { \pefree_rel(p, (ht)->persistent); \return FAILURE; \} \memcpy((p)->pData, pData, nDataSize); \(p)->pDataPtr=NULL; \}這里有一個tricks,PHP判斷數據的大小和一個void指針相同時,就不為其申請額外的空間,而是將數據copy到pDataPtr字段中,也就是 說,如果你add到HashTable的是一個指針,那么他直接被保存在pDataPtr字段中,同時pData字段也會保存一份。如果你add到 HashTable的是一個更大的結構,那么PHP會為這個結構單獨申請內存空間,將數據copy到這片新申請的內存空間中,然后將pDataPtr設置 為NULL。
?
轉載于:https://www.cnblogs.com/leezhxing/p/4838927.html
總結
以上是生活随笔為你收集整理的php Hash Table(四) Hash Table添加和更新元素的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 元素或为1或为-1的行列式的值的估计
- 下一篇: PHP 设计模式六大原则