Python 字典的查找算法是如何内部实现的?
mydi['foo']
如果字典有1,000,000个条目,会执行一次树搜索吗? 我应该期望基于键字符串的长度还是字典的大小来衡量性能? 或许将所有内容塞进字典中和编写一个针对5百万个字符串的树搜索索引一样好呢?
Python 字典的查找算法是如何内部实现的?
mydi['foo']
如果字典有1,000,000个条目,会执行一次树搜索吗? 我应该期望基于键字符串的长度还是字典的大小来衡量性能? 或许将所有内容塞进字典中和编写一个针对5百万个字符串的树搜索索引一样好呢?
以下是更接近实际情况的伪代码。假设字典具有一个包含键值对的 data
属性,以及一个表示分配的单元格数量的 size
属性。
def lookup(d, key):
perturb = j = hash(key)
while True:
cell = d.data[j % d.size]
if cell.key is EMPTY:
raise IndexError
if cell.key is not DELETED and (cell.key is key or cell.key == key):
return cell.value
j = (5 * j) + 1 + perturb
perturb >>= PERTURB
perturb
值保证在解决哈希冲突时最终使用哈希代码的所有位,但一旦它降级到 0 ,(5*j)+1
将最终触及表中的所有单元格。
size
比实际使用的单元格数量始终要大得多,因此哈希在键不存在时保证最终会命中一个空单元格(并且通常很快就会命中)。还有一个已删除值用于表示不应终止搜索但当前未使用的单元格。
至于您关于键字符串长度的问题,哈希字符串将查看字符串中的所有字符,但字符串还具有用于存储计算出的哈希的字段。因此,如果您每次都使用不同的字符串进行查询,则字符串长度可能会产生影响,但是如果您有一组固定的键并重新使用相同的字符串,则哈希在第一次使用后不会被重新计算。Python 获得了这方面的好处,因为大多数名称查找涉及字典,并且每个变量或属性名称的单个副本都存储在内部,因此每次访问属性 x.y
都会进行字典查找,但不会调用哈希函数。
def lookup(d, key):
'''dictionary lookup is done in three steps:
1. A hash value of the key is computed using a hash function.
2. The hash value addresses a location in d.data which is
supposed to be an array of "buckets" or "collision lists"
which contain the (key,value) pairs.
3. The collision list addressed by the hash value is searched
sequentially until a pair is found with pair[0] == key. The
return value of the lookup is then pair[1].
'''
h = hash(key) # step 1
cl = d.data[h] # step 2
for pair in cl: # step 3
if key == pair[0]:
return pair[1]
else:
raise KeyError, "Key %s not found." % key
哈希查找不使用树。它们使用哈希表,并且具有恒定时间的查找。它们将占用更多的空间(平均而言,我认为是两倍),但查找和插入时间会更快。
简单来说,对您的键进行md5处理,然后将其与您拥有的地址数量取模,这就是您保存或查找密钥的位置。无论集合有多大,只要没有重大冲突,它总是需要相同的时间,而一个好的哈希将避免这种情况。
答案1:此视频中解释了内部工作原理。
答案2:如果您的字典中有一百万条记录,则不会进行树搜索。
答案3:由于可能存在键冲突,您将希望以字典的大小而不是关键字字符串的长度来衡量性能。
答案4:可以将字典视为数组(连续的内存位置),但数组内可能存在未使用的块。 因此,与树相比,字典往往浪费大量内存空间。 但是,为了更好的运行时性能,字典可能比树更好。 键冲突有时会降低性能。 您应该阅读有关一致性哈希的信息。