各位观众老爷们,大家好!今天咱们聊聊JavaScript里一个挺低调但贼好使的家伙——Set
。别看它名字简单,用途可大了去了,尤其是在去重和各种集合操作方面,简直是神器级别的存在。
Set是个啥玩意儿?
简单来说,Set
就是JavaScript里用来存储唯一值的集合。注意!是唯一值!也就是说,你往里头放重复的东西,它只会保留一份。这特性用来去重简直不要太爽。
你可以把它想象成一个不允许有双胞胎的俱乐部,谁要是想偷偷溜进来一个长得一模一样的,立马被踢出去。
怎么用Set?
创建Set
非常简单:
const mySet = new Set();
现在 mySet
就是一个空的集合了,等着你往里头塞东西。
往Set里添加元素
用 add()
方法往 Set
里添加元素:
mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(1); // 再次添加1,会被忽略
console.log(mySet); // 输出: Set(3) { 1, 2, 3 }
看到了吧?即使我们添加了两次 1
,Set
里也只会保留一个。这就是唯一性的威力。
检查Set里有没有某个元素
用 has()
方法来检查 Set
里有没有某个元素,返回 true
或 false
:
console.log(mySet.has(2)); // 输出: true
console.log(mySet.has(4)); // 输出: false
从Set里删除元素
用 delete()
方法来删除 Set
里的元素:
mySet.delete(2);
console.log(mySet); // 输出: Set(2) { 1, 3 }
Set的大小
用 size
属性来获取 Set
里有多少个元素:
console.log(mySet.size); // 输出: 2
清空Set
用 clear()
方法来清空 Set
里的所有元素:
mySet.clear();
console.log(mySet.size); // 输出: 0
Set的迭代
Set
是可迭代的,也就是说你可以用 for...of
循环来遍历它的元素:
const mySet = new Set([1, 2, 3, 4, 5]);
for (const item of mySet) {
console.log(item);
}
// 输出:
// 1
// 2
// 3
// 4
// 5
你也可以使用 forEach()
方法:
mySet.forEach(item => {
console.log(item);
});
// 输出:
// 1
// 2
// 3
// 4
// 5
Set的用武之地:去重
这绝对是 Set
最常用的场景之一。 给定一个数组,里面有很多重复的元素,怎么快速地把它去重呢?
const myArray = [1, 2, 2, 3, 4, 4, 5, 5, 5];
const uniqueArray = [...new Set(myArray)]; // 使用扩展运算符将Set转换回数组
console.log(uniqueArray); // 输出: [ 1, 2, 3, 4, 5 ]
是不是很简单粗暴? 先用 new Set(myArray)
创建一个 Set
,它会自动把重复的元素去掉。 然后用扩展运算符 ...
把 Set
转换回数组。搞定!
Set的进阶用法:集合操作
Set
不仅仅能去重,还能进行各种集合操作,比如并集、交集、差集等等。
-
并集 (Union)
并集是指两个集合的所有元素合并在一起,重复的元素只保留一个。
function union(setA, setB) { const unionSet = new Set(setA); // 先复制一份 setA for (const elem of setB) { unionSet.add(elem); // 把 setB 里的元素添加到 unionSet 里 } return unionSet; } const setA = new Set([1, 2, 3]); const setB = new Set([3, 4, 5]); const unionSet = union(setA, setB); console.log(unionSet); // 输出: Set(5) { 1, 2, 3, 4, 5 }
-
交集 (Intersection)
交集是指两个集合共有的元素。
function intersection(setA, setB) { const intersectionSet = new Set(); for (const elem of setB) { if (setA.has(elem)) { intersectionSet.add(elem); // 如果 setA 里也有这个元素,就添加到 intersectionSet 里 } } return intersectionSet; } const setA = new Set([1, 2, 3]); const setB = new Set([3, 4, 5]); const intersectionSet = intersection(setA, setB); console.log(intersectionSet); // 输出: Set(1) { 3 }
-
差集 (Difference)
差集是指存在于一个集合,但不存在于另一个集合的元素。
function difference(setA, setB) { const differenceSet = new Set(setA); // 先复制一份 setA for (const elem of setB) { differenceSet.delete(elem); // 把 setA 里和 setB 相同的元素删掉 } return differenceSet; } const setA = new Set([1, 2, 3]); const setB = new Set([3, 4, 5]); const differenceSet = difference(setA, setB); console.log(differenceSet); // 输出: Set(2) { 1, 2 } (A相对于B的差集)
-
对称差集 (Symmetric Difference)
对称差集是指只存在于其中一个集合,但不同时存在于两个集合的元素。 简单来说,就是并集减去交集。
function symmetricDifference(setA, setB) { const unionSet = new Set(setA); for(const elem of setB){ unionSet.add(elem); } const intersectionSet = new Set(); for (const elem of setB) { if (setA.has(elem)) { intersectionSet.add(elem); } } const symmetricDifferenceSet = new Set(unionSet); for(const elem of intersectionSet){ symmetricDifferenceSet.delete(elem); } return symmetricDifferenceSet; } const setA = new Set([1, 2, 3]); const setB = new Set([3, 4, 5]); const symmetricDifferenceSet = symmetricDifference(setA, setB); console.log(symmetricDifferenceSet); // 输出: Set(4) { 1, 2, 4, 5 }
-
子集 (Subset)
判断一个集合是否是另一个集合的子集。
function isSubset(setA, setB) { if (setA.size > setB.size) { return false; // 如果 setA 比 setB 大,肯定不是子集 } for (const elem of setA) { if (!setB.has(elem)) { return false; // 如果 setA 里有任何一个元素不在 setB 里,就不是子集 } } return true; } const setA = new Set([1, 2]); const setB = new Set([1, 2, 3]); console.log(isSubset(setA, setB)); // 输出: true console.log(isSubset(setB, setA)); // 输出: false
-
超集 (Superset)
判断一个集合是否是另一个集合的超集(包含另一个集合的所有元素)。
function isSuperset(setA, setB) { if (setA.size < setB.size) { return false; // 如果 setA 比 setB 小,肯定不是超集 } for (const elem of setB) { if (!setA.has(elem)) { return false; // 如果 setB 里有任何一个元素不在 setA 里,就不是超集 } } return true; } const setA = new Set([1, 2, 3]); const setB = new Set([1, 2]); console.log(isSuperset(setA, setB)); // 输出: true console.log(isSuperset(setB, setA)); // 输出: false
Set的注意事项
- Set 里的元素是无序的。虽然你添加元素的顺序会影响迭代的顺序,但
Set
本身并不保证元素的顺序。 - Set 只能存储唯一值。如果你需要存储重复的值,请使用数组或其他数据结构。
-
Set 可以存储任何类型的值,包括原始类型(如数字、字符串、布尔值)和对象。但是,对于对象来说,
Set
判断唯一性的标准是对象的引用是否相同,而不是对象的内容是否相同。const obj1 = { name: 'Alice' }; const obj2 = { name: 'Alice' }; const mySet = new Set(); mySet.add(obj1); mySet.add(obj2); // obj1 和 obj2 是不同的对象,所以都会被添加到 Set 里 console.log(mySet.size); // 输出: 2
Set和其他数据结构的对比
特性 | Set | Array | Object | Map |
---|---|---|---|---|
存储 | 唯一值集合 | 有序的值列表 | 键值对集合 | 键值对集合 |
唯一性 | 保证元素唯一 | 不保证元素唯一 | 键唯一,值可以重复 | 键唯一,值可以重复 |
顺序 | 无序 (迭代顺序取决于插入顺序) | 有序 | 无序 (ES6 之后迭代顺序取决于插入顺序) | 有序 (迭代顺序取决于插入顺序) |
查找元素 | has() 方法, O(1) 平均时间复杂度 |
indexOf() , includes() , O(n) 时间复杂度 |
hasOwnProperty() , O(1) 平均时间复杂度 |
has() , O(1) 平均时间复杂度 |
添加元素 | add() , O(1) 平均时间复杂度 |
push() , unshift() , O(1) 或 O(n) |
obj[key] = value , O(1) 平均时间复杂度 |
set() , O(1) 平均时间复杂度 |
删除元素 | delete() , O(1) 平均时间复杂度 |
pop() , shift() , splice() , O(n) |
delete obj[key] , O(1) 平均时间复杂度 |
delete() , O(1) 平均时间复杂度 |
迭代 | 可迭代 | 可迭代 | ES6 之前不可直接迭代,ES6 之后可迭代键 | 可迭代 |
用途 | 去重, 集合操作 | 存储有序数据, 遍历 | 存储键值对, 对象属性 | 存储键值对, 更灵活的键类型 (可以是对象) |
Set的实际应用场景
- 用户权限管理:可以用
Set
来存储用户的角色或权限,方便进行权限判断。 - 跟踪已访问的URL:防止重复请求相同的URL。
- 缓存:可以用
Set
来记录哪些数据已经被缓存,避免重复计算。 - 数据分析:统计不同用户的行为,去除重复的行为记录。
- 算法题:很多算法题都可以用
Set
来简化代码,比如判断一个数组里是否有重复的元素。
Set的高级技巧
-
使用Set进行字符串去重
const str = "abracadabra"; const uniqueStr = [...new Set(str)].join(''); console.log(uniqueStr); // 输出: "abrcd"
-
使用Set进行DOM元素去重
const elements = document.querySelectorAll('.my-element'); // 获取所有class为my-element的元素 const uniqueElements = [...new Set(elements)]; // 去重 console.log(uniqueElements.length); // 输出去重后的元素数量
-
配合其他数据结构使用
Set
可以和其他数据结构(如数组、对象、Map)配合使用,解决更复杂的问题。 比如说,你可以用Map
来存储每个元素出现的次数,然后用Set
来存储只出现一次的元素。
总结
Set
是 JavaScript 里一个非常实用且高效的数据结构。 它最大的特点就是存储唯一值,可以用来去重和进行各种集合操作。 掌握 Set
的用法可以让你写出更简洁、更高效的代码。 希望今天的讲解对大家有所帮助!下次再见!