千万级去重应手写循环+set保序,避免set()丢序、dict.fromkeys()性能损耗及不可哈希类型报错;不可哈希数据需转为可哈希形式;超大数据须分块处理或借助外部工具。

直接用 set() 会丢顺序,但千万级数据不能靠它硬扛
对千万级原始列表直接调用 list(set(data)) 虽然快,但结果顺序完全不可控,且内存峰值可能翻倍——因为要同时存原始列表和集合副本。更关键的是,set 只支持可哈希类型,遇到字典、列表等嵌套结构直接报 TypeError: unhashable type。
实际工程中,90% 的“千万级去重”不是纯数字/字符串列表,而是带业务字段的记录(如用户行为日志、商品评论),必须保留首次出现顺序,或按时间戳保留最新一条。这时候 set() 就不是解法,而是陷阱。
dict.fromkeys() 是通用保序方案,但有隐性性能损耗
Python 3.7+ 中 dict.fromkeys(data) 确实能保序且平均 O(n),但它内部仍要为每个元素构造键值对(value 固定为 None),比单纯用集合记录“是否见过”多一次内存分配。在千万级数据下,这个开销会明显体现在 GC 压力和总耗时上。
- 实测:1000 万整数去重,
dict.fromkeys()比纯集合 + 循环慢约 15–20% - 它不支持自定义去重逻辑(比如只看某几个字段)
- 若数据含
None值,dict.fromkeys()会把所有None合并成一个键,导致误删
真正高效的做法:手写循环 + set 记录已见项
这是多数生产环境采用的方案,核心就三步:初始化空 seen = set()、遍历原数据、遇到新元素才追加到结果列表。它把哈希查找的 O(1) 优势用到极致,且完全可控。
示例代码:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
def dedup_keep_order(seq):
seen = set()
result = []
for item in seq:
if item not in seen:
seen.add(item)
result.append(item)
return result
注意点:
- 确保
item可哈希;若不可哈希(如字典),需先用json.dumps(item, sort_keys=True)或tuple(item.items())转成可哈希形式 - 不要用
[x for x in seq if x not in seen or seen.add(x)]这种一行式——它依赖or短路逻辑,可读性差,且seen.add(x)返回None容易引发误解 - 对超大序列,考虑用生成器分块处理,避免一次性加载全部到内存
当数据超出内存时,必须换工具链
如果原始数据是几亿行 CSV 或数据库表,Python 进程内存扛不住,硬拼算法没意义。这时要切换思路:
- 用
pandas.read_csv(..., chunksize=N)分批读取 + 全局seen集合,但要注意去重跨块问题(首次出现可能在第 2 块,却被第 1 块漏判) - 更稳的方式是走外部工具:
sort file.csv | uniq(Linux),或用 SQLite 的CREATE TABLE ... AS SELECT DISTINCT ... - 如果字段多、逻辑复杂(如“相同 user_id + action 类型,只留 timestamp 最大的那条”),直接用
pandas.DataFrame.drop_duplicates(subset=['user_id', 'action'], keep='last'),底层是 C 实现,比纯 Python 快得多
真正卡住效率的往往不是算法本身,而是数据落盘方式、I/O 缓冲策略、以及是否让重复判断脱离 Python 解释器——这点容易被忽略。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










