在 JavaScript 中,Set
和 Map
是两种常用的集合类型,它们都用于存储数据,但设计目的和应用场景截然不同。以下是两者的核心区别及适用场景:
一、核心差异概览
特性 | Set |
Map |
---|---|---|
存储结构 | 无序(遍历时保持插入顺序)的唯一值集合 | 键值对映射(键唯一,值可重复) |
元素类型 | 仅存储值(value) | 存储键值对(key-value) |
键的特性 | 无键概念,元素本身即值 | 键必须唯一,值可重复 |
键的类型 | 不适用 | 键可以是任意类型(包括对象、函数) |
重复处理 | 自动过滤重复值(基于 === ) |
键重复时会覆盖旧值 |
主要操作 | 添加(add() )、删除(delete() )、检查存在性(has() ) |
设置(set() )、获取(get() )、删除(delete() ) |
遍历方式 | values() 或直接用 for...of |
entries() 、keys() 、values() |
应用场景 | 去重、成员检测、维护唯一值集合 | 缓存、映射关系、需要键值关联的场景 |
二、具体差异详解
1. 存储结构与元素类型
Set:
仅存储值,且每个值必须唯一。const set = new Set([1, 2, 2, 3]); console.log([...set]); // [1, 2, 3](自动去重)
AI 代码解读Map:
存储键值对,键的唯一性由引用或值决定。const map = new Map(); map.set("name", "John"); map.set(1, "one"); // 键可以是任意类型 console.log(map.get("name")); // "John"
AI 代码解读
2. 键的类型与唯一性
Set:
无键概念,元素本身即值,重复值会被自动过滤。const set = new Set(); set.add({ }); set.add({ }); // 两个空对象引用不同,均被添加 console.log(set.size); // 2
AI 代码解读Map:
键可以是任意类型(包括对象、函数),重复键会覆盖旧值。const map = new Map(); const key1 = { }; const key2 = { }; map.set(key1, "value1"); map.set(key2, "value2"); // 两个空对象引用不同,视为不同键 console.log(map.size); // 2 // 重复键覆盖示例 map.set("name", "Alice"); map.set("name", "Bob"); // 覆盖前一个值 console.log(map.get("name")); // "Bob"
AI 代码解读
3. 常用方法对比
操作 | Set 方法 |
Map 方法 |
---|---|---|
添加元素 | set.add(value) |
map.set(key, value) |
删除元素 | set.delete(value) |
map.delete(key) |
检查存在性 | set.has(value) |
map.has(key) |
获取元素数量 | set.size |
map.size |
清空集合 | set.clear() |
map.clear() |
遍历元素 | set.forEach(value => {}) |
map.forEach((value, key) => {}) |
获取所有值 | set.values() 或直接 for...of |
map.values() |
获取所有键 | 不适用 | map.keys() |
获取所有键值对 | 不适用 | map.entries() 或直接 for...of |
三、应用场景对比
场景 | 推荐使用 Set |
推荐使用 Map |
---|---|---|
数组去重 | ✅ [...new Set(arr)] |
❌ 不适用 |
快速检查元素存在性 | ✅ set.has(value) (O(1)) |
❌ map.has(key) 需维护键值对 |
存储对象并按引用查找 | ✅ set.has(obj) |
✅ map.get(obj) |
统计频率 | ❌ 无法直接关联计数 | ✅ map.set(key, count + 1) |
缓存计算结果 | ❌ 无键值映射 | ✅ map.set(input, result) |
维护有序的唯一值集合 | ✅ 遍历时保持插入顺序 | ❌ 键值对结构更适合映射关系 |
四、性能差异
Set:
- 添加、删除、检查存在性的时间复杂度均为 O(1),适合频繁操作。
- 遍历时按插入顺序返回元素。
Map:
- 键值对操作(
set
、get
、delete
)的时间复杂度也为 O(1)。 - 遍历时按键的插入顺序返回键值对。
- 键值对操作(
五、示例对比
1. 数组去重
// 使用 Set
const arr = [1, 2, 2, 3];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3]
AI 代码解读
2. 统计字符出现次数
// 使用 Map
const str = "hello";
const charCount = new Map();
for (const char of str) {
charCount.set(char, (charCount.get(char) || 0) + 1);
}
console.log(charCount); // Map(4) { 'h' => 1, 'e' => 1, 'l' => 2, 'o' => 1 }
AI 代码解读
3. 缓存计算结果
// 使用 Map 缓存斐波那契数列结果
const cache = new Map();
function fib(n) {
if (n <= 1) return n;
if (cache.has(n)) return cache.get(n);
const result = fib(n - 1) + fib(n - 2);
cache.set(n, result);
return result;
}
console.log(fib(10)); // 55(仅计算一次,后续直接从缓存获取)
AI 代码解读
六、总结:如何选择?
使用
Set
:- 需要存储唯一值集合。
- 频繁检查元素是否存在。
- 无需键值映射,只需维护值的集合。
使用
Map
:- 需要建立键值对映射关系。
- 键的类型可能是对象或其他引用类型。
- 需要按插入顺序遍历键值对。
理解两者差异后,可根据具体需求灵活选择数据结构,提升代码效率和可读性。