在 JavaScript 中,Set
和 Map
的底层实现均基于哈希表(Hash Table),因此在大多数操作上性能表现相近,但由于设计目的不同,仍存在细微差异。以下是两者性能的详细对比及适用场景分析:
一、核心操作的时间复杂度
操作 | Set |
Map |
说明 |
---|---|---|---|
添加元素 | add(value) |
set(key, value) |
均为 O(1) 平均时间复杂度 |
删除元素 | delete(value) |
delete(key) |
均为 O(1) 平均时间复杂度 |
检查存在性 | has(value) |
has(key) |
均为 O(1) 平均时间复杂度 |
获取元素数量 | size |
size |
均为 O(1) 直接返回属性值 |
遍历所有元素 | values() |
entries() |
均为 O(n) 需遍历所有元素 |
清空集合 | clear() |
clear() |
均为 O(1) 重置内部状态 |
二、性能差异的具体场景
1. 添加与查找操作
Set:适用于存储单一值的集合,添加和查找时无需处理键值对映射,略快于 Map。
const set = new Set(); set.add(1); // 直接存储值,无需键 console.log(set.has(1)); // O(1),查找速度快
Map:需维护键值对映射,操作略复杂,但仍保持 O(1) 时间复杂度。
const map = new Map(); map.set("key", 1); // 需处理键值对 console.log(map.get("key")); // O(1),但多一步键的哈希计算
2. 键的类型对性能的影响
- Set:仅存储值,无需计算键的哈希值,性能稍优。
- Map:键可以是任意类型(包括对象),需为每个键计算哈希值,开销略高。
const objKey = { }; map.set(objKey, "value"); // 对象键需额外计算哈希值
3. 大数据量下的内存占用
- Set:仅存储值,内存占用相对较小。
- Map:需存储键值对,内存占用约为 Set 的两倍(相同数量的元素)。
三、性能测试示例
以下代码对比了 Set 和 Map 在大数据量下的插入和查找性能:
// 测试环境:Chrome 浏览器,100万条数据
const size = 1000000;
// 测试 Set
const set = new Set();
console.time("Set add");
for (let i = 0; i < size; i++) {
set.add(i);
}
console.timeEnd("Set add"); // 约 20-30ms
console.time("Set has");
for (let i = 0; i < size; i++) {
set.has(i);
}
console.timeEnd("Set has"); // 约 15-25ms
// 测试 Map
const map = new Map();
console.time("Map set");
for (let i = 0; i < size; i++) {
map.set(i, i);
}
console.timeEnd("Map set"); // 约 30-40ms
console.time("Map get");
for (let i = 0; i < size; i++) {
map.get(i);
}
console.timeEnd("Map get"); // 约 25-35ms
结果说明:
- 插入操作:Map 比 Set 慢约 30%,因需处理键值对。
- 查找操作:Map 比 Set 慢约 20%,因需额外处理键的哈希值。
四、性能优化建议
优先使用 Set 存储单一值集合
若只需存储唯一值且无需键值映射,Set 的性能和内存效率均优于 Map。仅在需要键值映射时使用 Map
Map 的优势在于支持任意类型的键,且遍历时保持插入顺序,适合缓存、映射关系等场景。避免频繁删除和重新添加元素
哈希表在删除元素后可能不会立即释放内存,频繁删除添加可能导致内存碎片。大数据量下的遍历优化
若需频繁遍历集合,Set 和 Map 的性能相近,但需注意 Map 返回的是键值对,处理开销略高。
五、总结:如何根据性能选择?
场景 | 推荐使用 Set |
推荐使用 Map |
---|---|---|
存储唯一值集合 | ✅ 性能和内存效率更高 | ❌ 内存占用多,操作略慢 |
快速检查元素存在性 | ✅ 略快于 Map | ❌ 键值对处理开销略高 |
需要键值映射关系 | ❌ 不支持键值对 | ✅ 设计初衷即为此场景 |
键的类型为对象或函数 | ❌ 无法通过对象查找 | ✅ 支持任意类型的键 |
大数据量下的频繁插入/删除 | ✅ 略优 | ❌ 略慢(但仍为 O(1)) |
理解两者性能差异后,可根据具体业务需求选择合适的数据结构,在保证功能正确性的同时优化性能。