JavaScript Set
什么是 Set?
Set 是 ES6 (ECMAScript 2015) 引入的一种新的数据结构,它允许你存储任何类型的唯一值,无论是原始值还是对象引用。Set 的特点是:元素不重复,可以快速查找、添加和删除元素。
与数组不同,Set 没有索引,元素顺序由添加顺序决定。Set 主要用于需要保证元素唯一性的场景。
创建 Set
基本语法
javascript
// 创建空 Set
const emptySet = new Set();
// 从数组创建 Set
const numbersSet = new Set([1, 2, 3, 4, 5]);
// 从字符串创建 Set
const lettersSet = new Set('hello');
console.log(emptySet); // Set(0) {}
console.log(numbersSet); // Set(5) {1, 2, 3, 4, 5}
console.log(lettersSet); // Set(4) {'h', 'e', 'l', 'o'}
注意在创建 lettersSet
时,虽然 'hello' 有 5 个字符,但结果 Set 只有 4 个元素,因为字母 'l' 重复了,而 Set 会自动去重。
Set 的基本操作
添加元素
使用 add()
方法向 Set 添加元素:
javascript
const colors = new Set();
colors.add('red');
colors.add('green');
colors.add('blue');
// 添加重复元素不会有效果
colors.add('red');
// 可以链式调用
colors.add('yellow').add('purple');
console.log(colors);
// 输出: Set(5) {'red', 'green', 'blue', 'yellow', 'purple'}
检查元素是否存在
使用 has()
方法检查元素是否存在:
javascript
const fruits = new Set(['apple', 'banana', 'orange']);
console.log(fruits.has('apple')); // true
console.log(fruits.has('grape')); // false
删除元素
使用 delete()
方法删除元素:
javascript
const animals = new Set(['cat', 'dog', 'bird']);
animals.delete('dog');
console.log(animals); // Set(2) {'cat', 'bird'}
console.log(animals.delete('fish')); // false (返回是否删除成功)
获取 Set 大小
使用 size
属性获取 Set 中元素的数量:
javascript
const numbers = new Set([1, 2, 3, 4, 5]);
console.log(numbers.size); // 5
numbers.add(6);
console.log(numbers.size); // 6
numbers.delete(1);
console.log(numbers.size); // 5
清空 Set
使用 clear()
方法删除 Set 中的所有元素:
javascript
const data = new Set([1, 2, 3]);
console.log(data.size); // 3
data.clear();
console.log(data.size); // 0
console.log(data); // Set(0) {}
遍历 Set
有多种方法可以遍历 Set 中的元素:
forEach 方法
javascript
const colors = new Set(['red', 'green', 'blue']);
colors.forEach((value, valueAgain, set) => {
console.log(value);
});
// 输出:
// red
// green
// blue
备注
在 Set 的 forEach
方法中,前两个参数实际上是相同的值。这是为了与 Map 和 Array 的 forEach
保持参数结构一致。
for...of 循环
javascript
const numbers = new Set([1, 2, 3, 4, 5]);
for (const num of numbers) {
console.log(num);
}
// 输出: 1 2 3 4 5
使用扩展运算符
javascript
const fruits = new Set(['apple', 'banana', 'orange']);
const fruitsArray = [...fruits];
console.log(fruitsArray); // ['apple', 'banana', 'orange']
Set 的常见应用场景
数组去重
这是 Set 最常见的应用之一:
javascript
const numbers = [1, 2, 3, 4, 4, 5, 5, 6];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5, 6]
字符串去重
可以快速去除字符串中的重复字符:
javascript
const text = "Mississippi";
const uniqueChars = [...new Set(text)].join('');
console.log(uniqueChars); // "Misp"
集合操作
使用 Set 可以实现数学中的集合操作:
并集
javascript
function union(setA, setB) {
const result = new Set(setA);
for (const elem of setB) {
result.add(elem);
}
return result;
}
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
const unionSet = union(setA, setB);
console.log([...unionSet]); // [1, 2, 3, 4, 5]
交集
javascript
function intersection(setA, setB) {
const result = new Set();
for (const elem of setA) {
if (setB.has(elem)) {
result.add(elem);
}
}
return result;
}
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
const intersectionSet = intersection(setA, setB);
console.log([...intersectionSet]); // [3, 4]
差集
javascript
function difference(setA, setB) {
const result = new Set(setA);
for (const elem of setB) {
result.delete(elem);
}
return result;
}
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
const differenceSet = difference(setA, setB);
console.log([...differenceSet]); // [1, 2]
记录已访问的项目
在算法中,Set 可以用来追踪已访问过的项目:
javascript
function findDuplicates(array) {
const seen = new Set();
const duplicates = new Set();
for (const item of array) {
if (seen.has(item)) {
duplicates.add(item);
} else {
seen.add(item);
}
}
return [...duplicates];
}
const numbers = [1, 2, 3, 1, 4, 2, 5];
console.log(findDuplicates(numbers)); // [1, 2]
实际案例:用户交互记录系统
假设我们正在开发一个页面浏览跟踪系统,需要记录用户访问过的唯一页面:
javascript
class PageTracker {
constructor() {
this.visitedPages = new Set();
}
recordVisit(page) {
this.visitedPages.add(page);
}
hasVisited(page) {
return this.visitedPages.has(page);
}
get uniquePageCount() {
return this.visitedPages.size;
}
get allPages() {
return [...this.visitedPages];
}
}
// 使用示例
const tracker = new PageTracker();
// 用户浏览不同页面
tracker.recordVisit('/home');
tracker.recordVisit('/products');
tracker.recordVisit('/about');
tracker.recordVisit('/products'); // 重复访问
tracker.recordVisit('/contact');
console.log(`访问的唯一页面数: ${tracker.uniquePageCount}`); // 4
console.log(`是否访问过首页: ${tracker.hasVisited('/home')}`); // true
console.log(`是否访问过登录页: ${tracker.hasVisited('/login')}`); // false
console.log(`所有访问过的页面: ${tracker.allPages}`);
// 输出: 所有访问过的页面: /home,/products,/about,/contact
Set 与 Array 的比较
操作 | Set | Array |
---|---|---|
添加元素 | set.add(item) | array.push(item) |
检查元素是否存在 | set.has(item) - O(1) | array.includes(item) - O(n) |
删除元素 | set.delete(item) - O(1) | array.splice(index, 1) - O(n) |
获取大小 | set.size | array.length |
元素唯一性 | 自动去重 | 允许重复 |
元素访问 | 没有索引,需遍历 | 通过索引直接访问 |
提示
当需要频繁检查元素是否存在,或需要保证元素唯一性时,Set 比 Array 更高效。
Set 对象的局限性
- 没有内置的方法直接获取 Set 中的某个元素(没有索引)
- 不能像数组那样直接通过索引修改某个元素
- 不提供过滤、映射等数组具有的高阶函数
警告
如果需要这些功能,可以先将 Set 转换为数组,操作后再转回 Set。
javascript
const mySet = new Set([1, 2, 3, 4, 5]);
// 转为数组后使用数组方法
const doubledArray = [...mySet].map(x => x * 2);
// 再转回 Set
const doubledSet = new Set(doubledArray);
console.log([...doubledSet]); // [2, 4, 6, 8, 10]
总结
JavaScript Set 是一个非常有用的数据结构,尤其适合需要存储唯一值的场景。它提供了高效的添加、删除和查找操作,是处理唯一性需求的理想选择。
Set 的主要特点包括:
- 值的唯一性
- 快速的元素查找
- 简洁的 API
- 适合集合操作
通过掌握 Set,你可以更有效地处理数据去重、检查存在性以及其他需要唯一集合的场景。
练习
- 编写一个函数,找出两个数组中的共同元素。
- 创建一个函数,统计字符串中每个字符出现的次数。
- 使用 Set 实现一个简单的标签系统,可以添加和删除标签,并确保标签不重复。
- 实现一个函数,找出数组中所有重复出现的元素。
- 使用 Set 优化检查回文字符串的函数(提示:考虑字符出现次数的奇偶性)。
进一步学习资源
- MDN Web Docs: Set
- ECMAScript 规范: Set Objects
- 探索 Set 的姊妹数据结构:Map、WeakSet 和 WeakMap