python-字典的搜索策略函数

375 阅读2分钟

在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。 插入结果如下: