类型 | list | dict |
---|---|---|
索引 | 自然数i | 不可类型值key |
添加 | append、extend、insert | b[k]=v |
删除 | pop、remove* | pop |
更新 | a[i]=v | b[k]=v |
正查 | a[i]、a[i:j] | b[k]、copy |
反查 | index(v)、count(v) | 无 |
其它 | reverse、sort | has_key、update |
80/20 准则:80%的功能其使用率只有20%
由于列表的随机访问特性,这两个操作执行时间与列表大小无关,均为O(1)
lst.append(v),执行时间是O(1)
lst=lst+[v],执行时间是O(n+k),其中k是被加的列表长度
选择哪个方法来操作列表,决定了程序的性能
def test1():
l = []
for i in range(1000):
l = l + [i]
def test2():
l = []
for i in range(1000):
l.append(i)
def test3():
l = [i for i in range(1000)]
def test4():
l = list(range(1000))
from timeit import Timer
t1 = Timer("test1()", "from main import test1")
print("concat %f seconds\n" % t1.timeit(number=1000))
t2 = Timer("test2()", "from main import test2")
print("append %f seconds\n" % t2.timeit(number=1000))
t3 = Timer("test3()", "from main import test3")
print("comprehension %f seconds\n" % t3.timeit(number=1000))
t4 = Timer("test4()", "from main import test4")
print("list range %f seconds\n" % t4.timeit(number=1000))
list range最快 > 列表推导式 > append > concat 相加
concat 1.818228 seconds
append 0.090310 seconds
comprehension 0.040583 seconds
list range 0.017432 seconds
Operation | Big-O Efficiency |
---|---|
index[] | O(1) |
index assignment | O(1) |
append | O(1) |
pop() | O(1) |
pop(i) | O(n) |
insert(i, item) | O(n) |
del operator | O(n) |
iteration | O(n) |
contains(in) | O(n) |
get slice[x:y] | O(k) |
get slice | O(k) |
set slice[x:y] | O(n+k) |
reverse | O(n) |
concatenate | O(k) |
sort | O(n log n) |
multiply | O(nk) |
从中部移除元素的话,要把移除元素后面的元素全部向前挪位复制一遍,这个看起来有点笨拙,但这种实现方法能够保证列表按索引取值和赋值的操作很快,达到O(1),这也算是一种对常用和不常用操作的折衷方案。
相对同一个大小的list,分别调用pop()和pop(0)
pop()的时间不随list大小变化,pop(0)的时间随着list变大而变长
from timeit import Timer
popzero = Timer("x.pop(0)", "from main import x")
print("popzero %f seconds\n" % popzero.timeit(number=1000))
popend = Timer("x.pop()", "from main import x")
print("popend %f seconds\n" % popend.timeit(number=1000))
from timeit import Timer
popzero = Timer("x.pop(0)", "from __main__ import x")
popend = Timer("x.pop()", "from __main__ import x")
print("pop(0) pop()")
for i in range(1000000, 100000001, 1000000):
x = list(range(i))
pt = popend.timeit(number=1000)
x = list(range(i))
pz = popzero.timeit(number=1000)
print("%15.5f, %15.5f"%(pz, pt))
if __name__ == '__main__':
x = list(range(2000000))
输出结果:
pop()是平坦的常数
pop(0)是线性增长的趋势
最常用的取值get和赋值set,其性能为O(1),另一个重要操作contains(in)是判断字典中是否存在某个关键码(key),这个性能也是O(1)
operation | Big-O Efficiency |
---|---|
copy | O(n) |
get item | O(1) |
set item | O(1) |
delete item | O(1) |
contains(in) | O(1) |
iteration | O(n) |
生成包含连续值的list和包含连续关键码key的dict,用随机数来检验操作符in的耗时。
for i in range(10000, 1000001, 20000):
t = Timer("random.randrange(%d) in x" % i, "from __main__ import random, x")
x = list(range(i))
lst_time = t.timeit(number=1000)
x = {j:None for j in range(i)}
d_time = t.timeit(number=1000)
print("%d, %10.3f, %10.3f" % (i, lst_time, d_time))
输出结果: