为什么在对Python的集合进行线性遍历(使用any())时,明显比对列表慢?
背景:
我在比较在 list 上使用 any() 与在 set 上使用 any() 来查找一个已知元素的性能差异。我的初始假设是,因为 any() 会短路,性能应该相当,但我的基准测试显示 set 一直慢得多。
代码:
import timeit
large_list = ["target"] + [str(i) for i in range(10_000_000)]
large_set = set(large_list)
# Benchmarking
list_time = timeit.timeit(lambda: any(x == "target" for x in large_list), number=10)
set_time = timeit.timeit(lambda: any(x == "target" for x in large_set), number=10)
print(f"List: {list_time}")
print(f"Set: {set_time}")
到目前为止我的理解:
我知道在 large_set 中的 item 是O(1),并且这是检查成员关系的“正确”方式。
我也明白 any() 会强制执行线性扫描,因此这两种结构的复杂度都是O(n)。
问题:
在迭代时,具体是什么原因导致性能差距?是哈希表的内部内存布局(指针追逐)与列表的连续内存布局,还是集合的迭代实现存在特定的开销?
解决方案
因为集合中的顺序是未定义的。值 "target" 可能已经在集合中的某处移动,而你在遍历集合中的所有值,直到遇到该值。另一方面,在列表中,顺序是固定的,值始终在最前面,迭代时你遇到的第一个值就是它。
集合的优势恰恰在于你可以使用成员运算符在不对集合进行遍历的情况下检查成员性:
def test_set():
return "target" in large_set
这将始终很快。相反,你可以通过像下面这样构造列表来使测试彻底失败:
large_list = [str(i) for i in range(10_000_000)] + ["target"]
但上面固定的 test_set 仍然同样快。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。