用 set() 转换再 & 运算是求超大列表交集并去重的最快最稳妥方式,时间复杂度 o(min(len(a), len(b))),需确保元素可哈希且类型一致。

用 set() 转换再 & 运算,是最快且最稳妥的方式
对两个超大列表(比如各含百万级元素)求交集并去重,set 是唯一合理选择。Python 的 set 底层基于哈希表,& 操作平均时间复杂度为 O(min(len(a), len(b))),远优于嵌套循环或 list 的 in 查找(O(n×m))。
常见错误是先用 list(set(a)) 去重再求交——这会丢失原始顺序且无必要;更糟的是直接写 [x for x in a if x in b],在大列表上会卡死。
- 必须确保列表元素可哈希(如
int、str、tuple),含dict或list会报TypeError: unhashable type - 如果原列表有重复,
set(a) & set(b)自动完成“交集 + 去重”两件事,无需额外操作 - 结果是
set类型,如需转回list,用list(set_a & set_b)即可(但顺序不保证)
当需要保持交集元素在 a 中的首次出现顺序时
原生 set 不保留顺序,若业务要求结果按 a 中顺序排列(例如做数据对齐或 pipeline 输入),就不能只靠 &。
此时推荐用 set 加遍历:先构建 set(b) 用于 O(1) 查找,再单趟扫描 a,用 seen = set() 控制去重。
set_b = set(b)
result = []
seen = set()
for x in a:
if x in set_b and x not in seen:
result.append(x)
seen.add(x)
- 比
list(dict.fromkeys([x for x in a if x in set_b]))更省内存,避免中间列表爆炸 - 注意
x in set_b必须放在x not in seen前面,否则短路逻辑失效 - 如果
a极长但交集极小,这个方案实际更快,因为只遍历一次a,且提前过滤
内存吃紧时,用生成器 + 分块处理避免一次性加载
当列表大到无法全部载入内存(例如从文件流式读取的千万级记录),强行 set(a) 会触发 MemoryError。
这时应放弃“全量交集”,改用外存辅助或分块策略。最简可行解是把较小的列表转成 set,较大列表分批读入处理:
# 假设 b 较小,a 太大只能迭代
set_b = set(b)
def chunked_intersection(large_iter, chunk_size=10000):
seen = set()
for chunk in iter(lambda: list(islice(large_iter, chunk_size)), []):
for x in chunk:
if x in set_b and x not in seen:
yield x
seen.add(x)
-
set_b必须是小集合,否则分块没意义;估算内存:100 万个int约占 20–30MB - 不要用
yield from直接返回[x for x in chunk if x in set_b],那会重复产出相同元素 - 若两端都超大且无法预估大小,就得引入磁盘排序(如 Unix
sort | uniq)或数据库临时表
别忽略类型隐式转换导致的“假不相等”
看似相同的值,因类型不同被 set 当作不同元素,这是线上最容易被忽略的坑。比如 '123' 和 123、True 和 1、浮点数精度误差(0.1 + 0.2 != 0.3)。
- 交集前务必确认两端数据类型一致,用
type(x)抽样检查几个元素 - 字符串数字统一转
int或float时,注意异常(ValueError),建议用try/except或pandas.to_numeric(..., errors='coerce') - 浮点数慎用
set交集,优先转为round(x, 5)或用math.isclose配合自定义逻辑(但无法用&直接加速)
真正的大数据交集,90% 的性能损耗不在算法,而在类型清洗和边界校验。先跑通 set(a) & set(b),再花三倍时间检查数据一致性,比反复调优代码有用得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











