跳到主要内容

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 的比较

操作SetArray
添加元素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.sizearray.length
元素唯一性自动去重允许重复
元素访问没有索引,需遍历通过索引直接访问
提示

当需要频繁检查元素是否存在,或需要保证元素唯一性时,Set 比 Array 更高效。

Set 对象的局限性

  1. 没有内置的方法直接获取 Set 中的某个元素(没有索引)
  2. 不能像数组那样直接通过索引修改某个元素
  3. 不提供过滤、映射等数组具有的高阶函数
警告

如果需要这些功能,可以先将 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,你可以更有效地处理数据去重、检查存在性以及其他需要唯一集合的场景。

练习

  1. 编写一个函数,找出两个数组中的共同元素。
  2. 创建一个函数,统计字符串中每个字符出现的次数。
  3. 使用 Set 实现一个简单的标签系统,可以添加和删除标签,并确保标签不重复。
  4. 实现一个函数,找出数组中所有重复出现的元素。
  5. 使用 Set 优化检查回文字符串的函数(提示:考虑字符出现次数的奇偶性)。

进一步学习资源