Python集合去重靠哈希表实现,平均时间复杂度O(1)每次插入,整体O(n);依赖对象可哈希性,不可变类型如int/str/tuple可入set,list/dict/set则不可;保序去重推荐dict.fromkeys(),浮点精度、自定义类未重写__hash__和__eq__、字符串未归一化是三大易错点。

Python 集合去重靠的是哈希表(hash table)底层实现,不是遍历比对。这意味着 set 去重平均时间复杂度是 O(1) 每次插入,整体 O(n),远快于用 list 手动查重的 O(n²)。
为什么 set 能秒级去重?
因为 set 内部用哈希表存储元素:每个元素先算 hash(),映射到固定桶位;冲突时用开放寻址或链地址法处理。只要对象可哈希(immutable 且实现 __hash__ 和 __eq__),就能进 set。
- 可哈希类型:
int、str、tuple(不含可变项)、frozenset - 不可哈希类型:
list、dict、set—— 直接加会报TypeError: unhashable type -
hash()值相同且__eq__返回True的对象,会被视为重复项
常见去重场景与写法对比
别无脑转 list(set(...)) —— 它不保序,还可能出错。
- 保序去重(首次出现优先):
用dict.fromkeys(items)最快最安全,Python 3.7+ 保证插入顺序
等价但更直观:用dict当“有序集合”容器 - 含嵌套结构(如
list)要去重:
先转成可哈希形式,例如tuple(item)(仅当item是一维list且元素可哈希)
或用frozenset处理无序集合型数据 -
大数据量时避免一次性构造:
用生成器 +set边读边判重:seen = set() unique_items = [] for item in large_iterable: if item not in seen: seen.add(item) unique_items.append(item)
set 去重的三个易踩坑点
这些错误不会报错,但结果不对,调试起来很隐蔽。
立即学习“Python免费学习笔记(深入)”;
- 浮点数精度导致哈希不一致:
0.1 + 0.2 != 0.3,进set可能被当成不同元素 - 自定义类没重写
__hash__和__eq__:默认按内存地址哈希,两个值相同的实例仍算不同 - 字符串首尾空格或大小写未归一化:
"abc "和"abc"是两个不同str,set不会自动 strip 或 lower
真正难的不是“怎么去重”,而是想清楚“什么才算重复”——哈希只认字面值相等,不理解业务语义。比如日期字符串 "2024-01-01" 和 datetime.date(2024, 1, 1),在 set 里永远不重复,哪怕你心里觉得它们代表同一天。










