JavaScript系列(9)-- Set数据结构专题
JavaScript Set数据结构专题 🎲
在前八篇文章中,我们探讨了JavaScript的语言特性、ECMAScript标准、引擎工作原理、数值类型、字符串处理、Symbol类型、Object高级特性和Array高级操作。今天,让我们深入了解JavaScript中的Set数据结构。Set是ES6引入的一种新的集合类型,它允许你存储任何类型的唯一值。
Set基础概念 🌟
💡 小知识:Set是一个值的集合,其中的每个值只能出现一次。这个特性使它特别适合用于去重和集合运算。与Array不同,Set不是索引集合,它更关注值的唯一性。
Set的创建和基本操作 📊
// 1. Set的创建方式
function setCreation() {
// 空Set
const set1 = new Set();
// 从数组创建
const set2 = new Set([1, 2, 3, 3, 4]); // 重复的3只会保留一个
// 从字符串创建
const set3 = new Set('hello'); // Set(4) {'h', 'e', 'l', 'o'}
// 从其他可迭代对象创建
const map = new Map([['a', 1], ['b', 2]]);
const set4 = new Set(map.keys());
// 基本操作
set1.add(1); // 添加值
set1.delete(1); // 删除值
set1.has(1); // 检查值是否存在
set1.clear(); // 清空Set
console.log(set1.size); // 获取大小
}
// 2. Set的迭代
function setIteration() {
const set = new Set(['a', 'b', 'c']);
// forEach方法
set.forEach((value, valueAgain, set) => {
console.log(value); // value和valueAgain是相同的
});
// for...of循环
for (const value of set) {
console.log(value);
}
// 转换为数组
const array = [...set];
const array2 = Array.from(set);
// 获取迭代器
const values = set.values();
const entries = set.entries();
// entries()返回[value, value]形式的条目
for (const [value1, value2] of entries) {
console.log(value1 === value2); // true
}
}
// 3. Set的值比较
function setValueComparison() {
const set = new Set();
// NaN的处理
set.add(NaN);
set.add(NaN);
console.log(set.size); // 1,NaN被认为是相同的值
// +0和-0的处理
set.add(+0);
set.add(-0);
console.log(set.size); // 1,+0和-0被认为是相同的值
// 对象的处理
const obj1 = { id: 1 };
const obj2 = { id: 1 };
set.add(obj1);
set.add(obj2);
console.log(set.size); // 2,不同对象被认为是不同的值
}
Set的高级操作 🔧
// 1. 集合运算
class SetOperations {
// 并集
static union(setA, setB) {
return new Set([...setA, ...setB]);
}
// 交集
static intersection(setA, setB) {
return new Set([...setA].filter(x => setB.has(x)));
}
// 差集
static difference(setA, setB) {
return new Set([...setA].filter(x => !setB.has(x)));
}
// 对称差集
static symmetricDifference(setA, setB) {
return new Set(
[...setA].filter(x => !setB.has(x))
.concat([...setB].filter(x => !setA.has(x)))
);
}
// 子集检查
static isSubset(setA, setB) {
return [...setA].every(x => setB.has(x));
}
// 超集检查
static isSuperset(setA, setB) {
return [...setB].every(x => setA.has(x));
}
}
// 2. 自定义Set操作
class CustomSet extends Set {
// 添加多个值
addMany(...items) {
items.forEach(item => this.add(item));
return this;
}
// 删除多个值
deleteMany(...items) {
items.forEach(item => this.delete(item));
return this;
}
// 过滤操作
filter(callback) {
const filteredSet = new CustomSet();
for (const value of this) {
if (callback(value)) {
filteredSet.add(value);
}
}
return filteredSet;
}
// 映射操作
map(callback) {
const mappedSet = new CustomSet();
for (const value of this) {
mappedSet.add(callback(value));
}
return mappedSet;
}
// 转换为对象
toObject(keyFn = value => value) {
const obj = {};
for (const value of this) {
obj[keyFn(value)] = value;
}
return obj;
}
}
// 3. Set与数组的互操作
class SetArrayOperations {
// 数组去重
static uniqueArray(arr) {
return [...new Set(arr)];
}
// 查找重复元素
static findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
arr.forEach(item => {
if (seen.has(item)) {
duplicates.add(item);
} else {
seen.add(item);
}
});
return duplicates;
}
// 保持插入顺序的唯一化
static uniqueOrdered(arr) {
return Array.from(new Set(arr));
}
}
Set的实际应用 💼
让我们看看Set在实际开发中的一些应用场景:
// 1. 标签系统
class TagSystem {
constructor() {
this.tagSets = new Map();
}
// 添加标签
addTags(itemId, ...tags) {
if (!this.tagSets.has(itemId)) {
this.tagSets.set(itemId, new Set());
}
tags.forEach(tag => this.tagSets.get(itemId).add(tag));
}
// 移除标签
removeTags(itemId, ...tags) {
const tagSet = this.tagSets.get(itemId);
if (tagSet) {
tags.forEach(tag => tagSet.delete(tag));
}
}
// 获取具有特定标签的项目
getItemsWithTags(...tags) {
const items = [];
for (const [itemId, tagSet] of this.tagSets) {
if (tags.every(tag => tagSet.has(tag))) {
items.push(itemId);
}
}
return items;
}
// 获取相关标签
getRelatedTags(tag) {
const relatedTags = new Set();
for (const tagSet of this.tagSets.values()) {
if (tagSet.has(tag)) {
tagSet.forEach(t => {
if (t !== tag) relatedTags.add(t);
});
}
}
return relatedTags;
}
}
// 2. 用户权限系统
class PermissionSystem {
constructor() {
this.userPermissions = new Map();
this.rolePermissions = new Map();
}
// 添加角色权限
addRolePermissions(role, ...permissions) {
if (!this.rolePermissions.has(role)) {
this.rolePermissions.set(role, new Set());
}
permissions.forEach(perm =>
this.rolePermissions.get(role).add(perm)
);
}
// 分配用户角色
assignUserRoles(userId, ...roles) {
if (!this.userPermissions.has(userId)) {
this.userPermissions.set(userId, new Set());
}
const userPerms = this.userPermissions.get(userId);
roles.forEach(role => {
const rolePerms = this.rolePermissions.get(role);
if (rolePerms) {
rolePerms.forEach(perm => userPerms.add(perm));
}
});
}
// 检查权限
hasPermission(userId, permission) {
const userPerms = this.userPermissions.get(userId);
return userPerms ? userPerms.has(permission) : false;
}
}
// 3. 缓存系统
class CacheSystem {
constructor(maxSize = 1000) {
this.maxSize = maxSize;
this.cache = new Map();
this.accessOrder = new Set();
}
get(key) {
if (this.cache.has(key)) {
// 更新访问顺序
this.accessOrder.delete(key);
this.accessOrder.add(key);
return this.cache.get(key);
}
return null;
}
set(key, value) {
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
// 移除最早访问的项
const oldestKey = this.accessOrder.values().next().value;
this.accessOrder.delete(oldestKey);
this.cache.delete(oldestKey);
}
this.cache.set(key, value);
this.accessOrder.delete(key);
this.accessOrder.add(key);
}
}
性能优化 ⚡
处理Set时的一些性能优化技巧:
// 1. Set大小优化
function setSizeOptimization() {
// 预分配合适的大小
const initialData = new Array(1000).fill(0)
.map((_, i) => i);
// 一次性创建Set
const set = new Set(initialData); // 比循环添加更快
// 避免频繁修改
const tempArray = [];
for (let i = 0; i < 1000; i++) {
tempArray.push(i);
}
const set2 = new Set(tempArray);
}
// 2. Set操作优化
class OptimizedSetOperations {
// 优化的交集操作
static optimizedIntersection(setA, setB) {
// 选择较小的集合进行迭代
const [smaller, bigger] = setA.size < setB.size
? [setA, setB]
: [setB, setA];
return new Set([...smaller].filter(x => bigger.has(x)));
}
// 批量操作优化
static batchAdd(set, items) {
const tempSet = new Set(set);
items.forEach(item => tempSet.add(item));
return tempSet;
}
}
// 3. 内存优化
class MemoryEfficientSet {
constructor() {
this.set = new Set();
this.weakRefs = new WeakMap();
}
addObject(obj) {
const ref = new WeakRef(obj);
this.weakRefs.set(obj, ref);
this.set.add(ref);
}
hasObject(obj) {
const ref = this.weakRefs.get(obj);
return ref && this.set.has(ref) && ref.deref() === obj;
}
}
最佳实践建议 💡
- Set使用场景
// 1. 数据去重
function deduplication() {
// 基本类型去重
const numbers = [1, 2, 2, 3, 3, 4];
const uniqueNumbers = [...new Set(numbers)];
// 对象去重
const objects = [
{ id: 1, name: 'A' },
{ id: 1, name: 'A' },
{ id: 2, name: 'B' }
];
const uniqueById = [...new Set(
objects.map(obj => JSON.stringify(obj))
)].map(str => JSON.parse(str));
}
// 2. 集合运算
function setOperations() {
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
// 并集
const union = new Set([...set1, ...set2]);
// 交集
const intersection = new Set(
[...set1].filter(x => set2.has(x))
);
// 差集
const difference = new Set(
[...set1].filter(x => !set2.has(x))
);
}
// 3. 唯一性检查
function uniquenessChecking() {
const visited = new Set();
function isUnique(item) {
if (visited.has(item)) return false;
visited.add(item);
return true;
}
}
- 错误处理和验证
// 1. Set验证
function validateSet(set) {
if (!(set instanceof Set)) {
throw new TypeError('参数必须是Set类型');
}
if (set.size === 0) {
throw new Error('Set不能为空');
}
return true;
}
// 2. 安全的Set操作
function safeSetOperations() {
function safeAdd(set, value) {
try {
set.add(value);
return true;
} catch (error) {
console.error('添加值失败:', error);
return false;
}
}
function safeDelete(set, value) {
try {
return set.delete(value);
} catch (error) {
console.error('删除值失败:', error);
return false;
}
}
}
// 3. 类型检查
function setTypeChecking() {
function isNumberSet(set) {
return set instanceof Set &&
[...set].every(x => typeof x === 'number');
}
function isObjectSet(set) {
return set instanceof Set &&
[...set].every(x => x && typeof x === 'object');
}
}
结语 📝
JavaScript的Set数据结构为我们提供了一种优雅的方式来处理唯一值的集合。我们学习了:
- Set的基本概念和操作
- Set的高级操作和扩展
- 实际应用场景
- 性能优化技巧
- 最佳实践和注意事项
💡 学习建议:在使用Set时,要充分利用其唯一性特征,选择合适的场景使用。注意Set的值比较规则,特别是在处理对象时。同时,要考虑性能影响,合理使用Set的各种操作方法。
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻