下面分析散列表常见操作,如插入、删除、替换等。
散列表插入函数dictAdd实现如下:
/* Add an element to the target hash table */ int dictAdd(dict *d, void *key, void *val) { int index; dictEntry *entry; dictht *ht; if (dictIsRehashing(d)) _dictRehashStep(d); /* Get the index of the new element, or -1 if * the element already exists. */ if ((index = _dictKeyIndex(d, key)) == -1) return DICT_ERR; /* Allocates the memory and stores key */ ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; entry = zmalloc(sizeof(*entry)); entry->next = ht->table[index]; ht->table[index] = entry; ht->used++; /* Set the hash entry fields. */ dictSetHashKey(d, entry, key); dictSetHashVal(d, entry, val); return DICT_OK; }
如果该键的索引已经存在,返回错误。
下面为新项分配内存,如果进行了rehash,分配在ht[1],否则分配在ht[0]。
最后调用dictSetHashKey和dictSetHashVal设置键和值。这两个宏的定义如下:
#define dictSetHashKey(d, entry, _key_) do { \ if ((d)->type->keyDup) \ entry->key = (d)->type->keyDup((d)->privdata, _key_); \ else \ entry->key = (_key_); \ } while(0) #define dictSetHashVal(d, entry, _val_) do { \ if ((d)->type->valDup) \ entry->val = (d)->type->valDup((d)->privdata, _val_); \ else \ entry->val = (_val_); \ } while(0)
替换一个键值对的操作函数如下:
/* Add an element, discarding the old if the key already exists. * Return 1 if the key was added from scratch, 0 if there was already an * element with such key and dictReplace() just performed a value update * operation. */ int dictReplace(dict *d, void *key, void *val) { dictEntry *entry, auxentry; /* Try to add the element. If the key * does not exists dictAdd will suceed. */ if (dictAdd(d, key, val) == DICT_OK) return 1; /* It already exists, get the entry */ entry = dictFind(d, key); /* Free the old value and set the new one */ /* Set the new value and free the old one. Note that it is important * to do that in this order, as the value may just be exactly the same * as the previous one. In this context, think to reference counting, * you want to increment (set), and then decrement (free), and not the * reverse. */ auxentry = *entry; dictSetHashVal(d, entry, val); dictFreeEntryVal(d, &auxentry); return 0; }
如果原来已经存在了此键的项,那么首先用dictFind查找出此项,然后设置新值,释放原项。注意这里的顺序,因为新值可能和旧值完全一样,如果先释放原项可能会使引用计数不合法。
上面提到的dictFind查找函数实现如下:
dictEntry *dictFind(dict *d, const void *key) { dictEntry *he; unsigned int h, idx, table; if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */ if (dictIsRehashing(d)) _dictRehashStep(d); h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx]; while(he) { if (dictCompareHashKeys(d, key, he->key)) return he; he = he->next; } if (!dictIsRehashing(d)) return NULL; } return NULL; }
我们看到,首先调用一次哈希函数得到该键的索引h,然后在两个哈希表ht[0]、ht[1]中按链表查找。
最后,我们分析删除哈希表中一项的函数dictGenericDelete:
/* Search and remove an element */ static int dictGenericDelete(dict *d, const void *key, int nofree) { unsigned int h, idx; dictEntry *he, *prevHe; int table; if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */ if (dictIsRehashing(d)) _dictRehashStep(d); h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx]; prevHe = NULL; while(he) { if (dictCompareHashKeys(d, key, he->key)) { /* Unlink the element from the list */ if (prevHe) prevHe->next = he->next; else d->ht[table].table[idx] = he->next; if (!nofree) { dictFreeEntryKey(d, he); dictFreeEntryVal(d, he); } zfree(he); d->ht[table].used--; return DICT_OK; } prevHe = he; he = he->next; } if (!dictIsRehashing(d)) break; } return DICT_ERR; /* not found */ }