set去重需综合数据类型、语义与环境:基本类型依赖===相等;字符串需统一大小写;数字存在精度问题;nan在js中去重有效但python中不可哈希;对象数组须自定义哈希或序列化;顺序敏感场景应选linkedhashset/dict.fromkeys/array.from;null/undefined/0/-0等特殊值行为因语言而异。

Set 集合去重看似简单,但真正落地时,边界情况往往决定成败。它不是“调个构造函数就完事”,而是需要结合数据类型、语义要求和运行环境综合判断。
基本类型去重:可靠但有隐含前提
对数字、字符串、布尔值等可哈希的原始类型,new Set([...]) 或 set(...) 是高效且安全的。前提是这些值本身满足“严格相等(===)即语义相等”的业务逻辑。
- 字符串大小写敏感:若需忽略大小写,不能直接塞进 Set,得先统一转小写再处理
- 数字精度问题:
0.1 + 0.2 === 0.3为 false,用 Set 去重时0.30000000000000004和0.3会被视为两个不同元素 - NaN 的特殊性:JavaScript 中
NaN !== NaN,但new Set([NaN, NaN])只保留一个 NaN;Python 中set([float('nan'), float('nan')])却可能保留多个——因 NaN 不可哈希,部分实现会报错或行为不一致
对象与数组:默认不生效,必须主动建模
Set 对象元素的判重依赖 hashCode(Java)或哈希值(Python/JS),而默认情况下,两个内容完全相同的对象拥有不同内存地址,必然不重复。
- Java 必须重写
equals()和hashCode(),且二者逻辑一致(例如都基于 id 字段),否则HashSet形同虚设 - JavaScript 中
new Set([{a:1}, {a:1}])长度为 2;可用Map按唯一键缓存,或用JSON.stringify()生成指纹——但要注意属性顺序影响结果,生产环境慎用 - Python 中列表、字典不可作为 set 元素;如需对字典列表去重,可转为
frozenset(d.items())或tuple(sorted(d.items()))
顺序语义丢失:业务上最常被忽视的风险
多数 Set 实现(如 Java HashSet、Python set、JS Set)不保证插入顺序。当去重目标是事件流、日志序列、用户操作轨迹时,“谁先来”本身就是关键业务规则。
- 订单状态流
["created", "paid", "paid", "shipped"]经list(set(...))后顺序打乱,可能导致状态机校验失败 - 解决方案不是放弃 Set,而是换用保序结构:Java 用
LinkedHashSet,Python 用dict.fromkeys(...).keys()(3.7+ 保证插入序),JS 用Array.from(new Set(arr))(ES2015 起 Set 本身保持插入顺序,但仅限于首次出现位置) - 注意:JS 的 Set 插入顺序保障 ≠ 数组索引顺序;若数组本身含重复且非首现项,
[...new Set(arr)]返回的是按首次出现位置排列的结果,不是原数组顺序
空值与特殊值:容易引发 NPE 或逻辑跳变
null、undefined、空字符串、0、false 等在不同语言中参与 Set 运算时表现各异,极易埋下线上隐患。
- Java 中
HashSet允许存一个null,但TreeSet直接抛NullPointerException - Python 中
set([None, None, 1])正常,但若误写成{}(空字典)而非set(),后续.add()会报AttributeError - JS 中
new Set([null, undefined])包含两个独立元素;但0和-0在 Set 中被视为相同(ES6 规范),而Object.is(0, -0)为 true,这点常被忽略











