基本上,我需要创建一个包含非连续整数ID的查找表。我想知道,就查找速度而言,我通常最好不要使用带有整数键的dict
,或者使用带有大量空索引的非常长的list
。在我看来,list
可能仍然会更快,因为Python应该确切知道在哪里看,但我想知道是否有任何后端进程与dict
进行补偿以及是否有额外的内存对那些空list
个插槽的要求会否定(可能)更容易遍历list
的速度增益。是否有list
和dict
的替代品可能更适合这种情况?
我已经看到了这个问题,但它并没有完全回答我的问题:Dictionary access speed comparison with integer key against string key
ETA:我在我的程序中实现了两次这样的查找表。一个实例看到最大id为5,000,其中填充了70-100个对象;另一个的最大ID为750,其中20-30填充。
答案 0 :(得分:11)
要回答有关dict
vs list
的问题,您必须提供有关元素数量,缺失元素数量等的完全信息,因此,我们可以准确估计两个数据结构的内存使用情况,并尝试预测和/或检查它们的性能。
一般来说,dict
个N
键值对比list
值N
所需的内存要多得多:
dict
必须跟踪密钥dict
永远不会超过2/3。发生这种情况时,分配的内存加倍(这需要在dict
上进行O(1)摊销时间操作。 然而可以替代这些数据结构,它们应该提供非常好的性能:blist
。 blist
包提供了与list
匹配的接口,只有使用B树实现。它可以有效地处理稀疏列表。大多数操作都需要O(1)
或O(log n)
时间,因此它们非常有效。
例如,您可以先创建稀疏blist
:
from blist import blist
seq = blist([None])
seq *= 2**30 # create a 2**30 element blist. Instantaneous!
然后您只能设置具有值的索引:
for i, value in zip(indices, values):
seq[i] = value
完整文档为here。
请注意blist
提供了其他有效的操作,例如:
blist
s O(log n)
时间[i:j]
切片需要O(log n)
时间O(log n)
次操作O(log n)
次操作由于您提供了一些数字,以下是与dict
的对比方式:
>>> from blist import blist
>>> b = blist([None])
>>> b *= 5000
>>> for i in range(100):b[i] = i
...
>>> b.__sizeof__()
2660
>>> d = dict()
>>> for i in range(100):d[i] = i
...
>>> d.__sizeof__()
6216
>>> b = blist([None])
>>> b *= 750
>>> for i in range(30):b[i] = i
...
>>> b.__sizeof__()
1580
>>> d = dict()
>>> for i in range(30):d[i] = i
...
>>> d.__sizeof__()
1608
在这两种情况下,blist
占用的内存较少(在第一种情况下,占用等效dict
的内存的1/3)。请注意,blist
占用的内存也取决于索引是否连续(连续更好)。然而,即使使用随机索引:
>>> b = blist([None])
>>> b *= 5000
>>> import random
>>> for i in range(100):b[random.randint(0, 4999)] = i
...
>>> b.__sizeof__()
2916
它仍然比dict
好得多。
即使查找时间更好:
In [1]: from blist import blist
...: import random
...:
In [2]: b = blist([None])
In [3]: b *= 5000
In [4]: for i in range(100):b[random.randint(0, 4999)] = i
In [5]: %timeit b[0]
10000000 loops, best of 3: 50.7 ns per loop
In [6]: d = dict()
In [7]: for i in range(100):d[random.randint(0, 4999)] = i
In [10]: %timeit d[1024] # 1024 is an existing key in this dictionary
10000000 loops, best of 3: 70.7 ns per loop
In [11]: %timeit b[1024]
10000000 loops, best of 3: 50.7 ns per loop
请注意,list
需要大约47 ns
才能在我的计算机上查找索引,因此blist
在小型查找性能方面非常接近list
列出你所拥有的。
答案 1 :(得分:1)
<强>解释强>
1.列表末尾的append
和pop
速度很快
2.列表开头的insert
和pop
速度很慢(这两个函数后面有一个很重的操作)
3.最好使用collection.degue作为第二种情况。
的字典:强>
4.与列表相比,访问操作更快
循环浏览词典和列表:
iteritems()
方法同时检索密钥及其对应的值。答案 2 :(得分:1)
我认为这个问题没有一般性答案。它取决于整数id,可用内存和性能要求的重新分区。规则是:
这可能是一条经验法则:
dict的理论是一个数组,其索引是应用于键的散列函数的结果。 Python algorythm已正确优化,但它是一个通用的。如果您知道自己有特殊的重新分区,可以尝试找到一个专门适用于您的重新分区的哈希值。您可以在Hash functions上的维基百科文章或旧的标准C库hash