迭代器,在很多的高级语言都会有该实现,其作用就是实现对容器的遍历
迭代器结构:
typedef struct dictIterator {
dict *d;
long index;
int table, safe; // safe 是否是安全的迭代器
dictEntry *entry, *nextEntry;
/* unsafe iterator fingerprint for misuse detection. */
long long fingerprint;
} dictIterator;
通过safe来区分 safe=1为安全迭代器
entry与nextEntry两个指针分别指向Hash冲突后的两个父子节点。 如果在安全模式下, 删除了entry节点, nextEntry字段可以保证后续迭代数据不丢失
迭代器的种类
普通的迭代器:只遍历数据
安全的迭代器:遍历的同时删除数据
迭代器的操作:
获取迭代器
获取普通迭代器:
dictIterator *dictGetIterator(dict *d)
{
dictIterator *iter = zmalloc(sizeof(*iter));
iter->d = d;
iter->table = 0;
iter->index = -1;
iter->safe = 0;
iter->entry = NULL;
iter->nextEntry = NULL;
return iter;
}
不允许对数据进行操作,因为遍历迭代器时会获取dictGetIterator 会获取dictFingerprint ,时整个字典的指纹,如果遍历完成有指纹发生变化,则会导致错误。
这时你可能会问,为啥不是单线程的吗,为什么遍历有可能被其他修改了呢?其实,如果你仔细看,我们对字典的任何插入,查找等操作,都会调用rehashstep,这就有可能导致了字典指纹的变化。
获取安全迭代器:
其实只是相对普通迭代器设置safe标志为1
dictIterator *dictGetSafeIterator(dict *d) {
dictIterator *i = dictGetIterator(d);
i->safe = 1;
return i;
}
字典的增删改查操作会调用dictRehashStep函数进行渐进式rehash操作,但是我们看到
static void _dictRehashStep(dict *d) {
if (d->iterators == 0) dictRehash(d,1);
}
其实,rehash的时候,会判断是不是在安全迭代器模式,如果是则不会rehash,所以就不会导致重复遍历。
迭代器的遍历
dictEntry *dictNext(dictIterator *iter)
{
while (1) {
if (iter->entry == NULL) {
dictht *ht = &iter->d->ht[iter->table];
if (iter->index == -1 && iter->table == 0) {
if (iter->safe)
iter->d->iterators++;
else
iter->fingerprint = dictFingerprint(iter->d);
}
iter->index++;
if (iter->index >= (long) ht->size) {
if (dictIsRehashing(iter->d) && iter->table == 0) {
iter->table++;
iter->index = 0;
ht = &iter->d->ht[1];
} else {
break;
}
}
iter->entry = ht->table[iter->index];
} else {
iter->entry = iter->nextEntry;
}
if (iter->entry) {
/* We need to save the 'next' here, the iterator user
* may delete the entry we are returning. */
iter->nextEntry = iter->entry->next;
return iter->entry;
}
}
return NULL;
}
如果是安全模式:首次遍历iterators 会++ ,表示现在字典在安全遍历,会禁止了rehash
如果不是安全模式:首次则要记录字典的指纹信息。
字典指纹的获取:其实就是将字典转换为一个64位的数组,也可以理解是一种hash
long long dictFingerprint(dict *d) {
long long integers[6], hash = 0;
int j;
integers[0] = (long) d->ht[0].table;
integers[1] = d->ht[0].size;
integers[2] = d->ht[0].used;
integers[3] = (long) d->ht[1].table;
integers[4] = d->ht[1].size;
integers[5] = d->ht[1].used;
/* We hash N integers by summing every successive integer with the integer
* hashing of the previous sum. Basically:
*
* Result = hash(hash(hash(int1)+int2)+int3) ...
*
* This way the same set of integers in a different order will (likely) hash
* to a different number. */
for (j = 0; j < 6; j++) {
hash += integers[j];
/* For the hashing step we use Tomas Wang's 64 bit integer hash. */
hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;
hash = hash ^ (hash >> 24);
hash = (hash + (hash << 3)) + (hash << 8); // hash * 265
hash = hash ^ (hash >> 14);
hash = (hash + (hash << 2)) + (hash << 4); // hash * 21
hash = hash ^ (hash >> 28);
hash = hash + (hash << 31);
}
return hash;
}
释放迭代器
void dictReleaseIterator(dictIterator *iter)
{
if (!(iter->index == -1 && iter->table == 0)) {
if (iter->safe)
iter->d->iterators--;
else
assert(iter->fingerprint == dictFingerprint(iter->d));
}
zfree(iter);
}
释放迭代器要将安全模式下iterators++的值减回去,如果不是安全模式,则要判断字典是不是遍历的过程中被修改,修改则触发断言。
最后释放迭代器。