在PyDictObject中插入和删除操作,都先使用搜索策略函数查找元素,可谓万众瞩目。
(一)lookdict_string函数理解
static dictentry* lookdict_string(dictobject *mp, PyObject *key, register long hash)
{
register int i;
register unsigned int perturb;
register dictentry *freeslot;
register unsigned int mask = mp->ma_mask;
dictentry *ep0 = mp->ma_table;
register dictentry *ep;
if (!PyString_CheckExact(key)) {
mp->ma_lookup = lookdict;
return lookdict(mp, key, hash);
}
//[1]
i = hash & mask;
ep = &ep0[i];
//[2]
if (ep->me_key == NULL || ep->me_key == key)
return ep;
//[3]
if (ep->me_key == dummy)
freeslot = ep;
else
{
//[4]
if (ep->me_hash == hash && _PyString_Eq(ep->me_key,key))
{
return ep;
}
freeslot = NULL;
}
/* In the loop, me_key == dummy is by far (factor of 100s)the least likely outcome, so test for that last. */
for (perturb = hash; ; perturb >>= PERTURB_SHIFT)
{
i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];
//[5]
if (ep->me_key == NULL)
return freeslot == NULL ? ep : freeslot;
if (ep->me_key == key|| (ep->me_hash == hash&& ep->me_key != dummy&& _PyString_Eq(ep->me_key, key)))
return ep;
if (ep->me_key == dummy && freeslot == NULL)
freeslot = ep;
}
}
freeslot是用来指向探测序列中第一个处于 Dummy 态的 entry。具体如下
- hash指向的entry:ep //[1]
- me_key是null或者等于key,则终止搜索,返回该enty; //[2]
- me_key是dummy,则设置freeslot指向这个Dummy态的entry; //[3]因为后面可能还有我们想要的元素,所以还想继续查找下去
- 如果hash指向的entry没有找到key是null或者等于key的,则沿着探测函数继续检测下一个entry是否满足要求:ep;
- 在检查下一个entry之前,freeslot可能指向一个Dummy态的元素也可能为空;
- 下一个entry的me_key如果是null,也就还是Unused态,就判断freeslot是否非空,如果非空,则前面有个伪删除的元素,返回该Dummy态元素,充分利用存储空间。如果为空,就返回当前这个Unused态的元素; //[5]
- 下一个entry的me_key如果是key,则终止搜索,是我们想要查找的元素;
- 下一个entry的me_key如果是dummy,前面又没有遇到dummy元素,则freeslot肯定是空的,则让freeslot指向这个第一个dummy元素;如果前面有遇到dummy元素,很显然,freeslot不用重新赋值;
(二)一次搜索与插入的过程(直接copy书中的例子)
散列函数:Hash(x)=x mod 10
探测函数:x=x+1
假设列表中已一次插入了(4,4),(14,14),(24, 24),(34, 34),则加入元素后的 entry 为 :
删除元素对(14,14)之后,往table中插入(104,104)。那么,在搜索过程中,freeslot会指向(14,14)的元素对位置。
由于table中没有(104,104)元素对,所以搜索到最后是#8处于 Unused 态的 entry
。因为freeslot不为空,最终插入(104,104)元素对的位置是freeslot指向的entry。
如果后面再想插入(14,14),则搜索函数开始工作,最终返回的是处于 Unused 态的 entry。 插入结果如下: