Set 与 Map 深度解析:区别、特性与应用场景
一、核心定义
数据类型 | 存储内容 | 键/值要求 | 重复性规则 |
---|---|---|---|
Set |
唯一值的集合 | 只有值(无键) | 值不能重复 |
Map |
键值对的集合 | 键值任意类型(包括对象引用) | 键不能重复 |
简单对比:
- Set:类似数组,但值唯一
[1, 2, 3]
- Map:类似对象,但键可以是对象
{ [obj]: "data" }
二、关键特性对比
特性 | Set | Map |
---|---|---|
初始化方式 | new Set([1, 2, 3]) |
new Map([[k1,v1], [k2,v2]]) |
元素唯一性判断 | 值严格相等(=== ,但 NaN=NaN ) |
键严格相等(=== ,但 NaN=NaN ) |
元素访问 | 无索引,只能遍历或查存在 | 通过键访问:map.get(key) |
顺序性 | 插入顺序保留 | 插入顺序保留 |
常用方法 | add() , delete() , has() |
set() , get() , has() |
大小获取 | set.size |
map.size |
三、核心方法详解
Set 操作示例:
arduino
JavaScript
const set = new Set();
set.add("apple"); // 添加值
set.add("banana");
console.log(set.size); // 2
console.log(set.has("apple")); // true
set.delete("banana"); // 删除
set.clear(); // 清空
Map 操作示例:
c
JavaScript
const map = new Map();
const keyObj = { id: 1 };
map.set(keyObj, "data"); // 键为对象
map.set(123, "数字键"); // 键为数字
console.log(map.get(keyObj)); // "data"
map.delete(123); // 删除键
四、特殊行为
-
重复值处理:
-
Set 添加重复值自动忽略:
csharpJavaScript const set = new Set(); set.add(1).add(1); // 最终只有 1 个元素
-
Map 重复键会覆盖旧值:
cJavaScript map.set("key", "v1"); map.set("key", "v2"); // 值变为 v2
-
-
NaN 的特殊相等:
arduinoJavaScript const set = new Set(); set.add(NaN).add(NaN); // 只保留 1 个 NaN const map = new Map(); map.set(NaN, "test"); console.log(map.get(NaN)); // "test"(NaN 可作为键)
五、与弱引用版本对比:WeakSet 与 WeakMap
特性 | WeakSet | WeakMap | 与普通版本区别 |
---|---|---|---|
键要求 | 只接受对象作为值 | 只接受对象作为键 | ✅ 键/值必须是对象引用 |
可遍历性 | ❌ 不可遍历 | ❌ 不可遍历 | ✅ 无 size 、keys() 等方法 |
垃圾回收 | 元素无引用时自动回收 | 键对象无引用时自动回收 | ✅ 防止内存泄漏 |
应用场景 | 临时对象跟踪 | 对象关联元数据 |
典型场景:
kotlin
JavaScript
// WeakMap 存储私有数据
const privateData = new WeakMap();
class Person {
constructor(name) {
privateData.set(this, { name }); // this 作键
}
getName() {
return privateData.get(this).name;
}
}
六、应用场景指南
场景描述 | 推荐数据结构 | 原因 |
---|---|---|
存储唯一值(如用户ID) | Set |
自动去重,高效判断存在性 |
需要保留插入顺序的键值对 | Map |
比普通对象更可靠的顺序保证 |
键需为非字符串(如对象/函数) | Map |
普通对象键会自动转字符串 |
临时缓存对象(防内存泄漏) | WeakSet /WeakMap |
自动回收无引用的对象 |
为对象关联私有数据 | WeakMap |
键为对象本身,对象销毁时数据自动清除 |
交集/并集运算(如好友关系) | Set |
通过 union() , intersection() 等方法实现高效集合运算 |
七、性能关键点
-
查找效率:
Set.has(value)
和Map.has(key)
的时间复杂度为 O(1) (哈希表实现),远快于数组的includes()
(O(n))
-
与数组转换:
sqlJavaScript // Set → Array const arr = [...set]; // Array → Set (去重) const set = new Set(arr);
-
遍历优化:
arduinoJavaScript // Set 遍历 for (const value of set) { ... } // Map 遍历 for (const [key, value] of map) { ... }
总结
Set
:处理唯一值集合,替代数组去重,高效值存在性检查Map
:键值对存储的升级版,支持任意类型键,保留插入顺序WeakSet/WeakMap
:专为对象设计的弱引用容器,防止内存泄漏- 性能优先 :高频查找用
Set/Map
,数组遍历改用for...of
或传统for
优先选择
Set/Map
的场景:
- 需要维护插入顺序
- 键为非字符串
- 高频存在性检查
- 避免内存泄漏(用 Weak 版本)