Set 与 Map 数据结构:JavaScript 中新的集合类型

Set 与 Map:JavaScript 里的新玩具,比你想象的更好玩!

JavaScript 这门语言,就像一个不断成长的孩子,总会给你带来一些新的惊喜。以前我们用数组和对象来处理各种数据,虽然也能勉强应付,但总感觉有些地方不够灵活,效率也不够高。还好,ES6 带来了 Set 和 Map 这两个新朋友,它们就像乐高积木里的特殊零件,能让你的代码更加优雅,更加高效,也更加…有趣!

想象一下,你是一个幼儿园老师,每天都要点名。以前你可能得遍历整个花名册,一个个比对,生怕漏掉哪个小朋友。现在有了 Set,你只需要把到场的小朋友名字放进 Set 里,然后检查花名册上的名字是否在 Set 里就行了,重复的名字还会自动帮你过滤掉,简直不要太省心!

再想象一下,你要做一个简单的英汉词典。以前你可能用对象来存储单词和释义,但对象的键只能是字符串,如果我想用一个复杂的对象作为键,那就抓瞎了。现在有了 Map,你可以把任何东西都当做键,甚至包括另一个对象!这就像你的百宝箱,想放什么就放什么,再也不用担心空间不够了。

好了,废话不多说,让我们一起走进 Set 和 Map 的世界,看看它们到底有多好玩!

Set:去重小能手,集合操作一把抓

Set,顾名思义,就是集合的意思。它最大的特点就是里面的元素都是唯一的,不允许重复。这就像一个严格的俱乐部,只有符合条件的人才能加入,如果你已经加入了,别人就不能再冒充你。

1. 创建 Set:

创建一个 Set 非常简单,只需要使用 new Set() 就可以了。

const mySet = new Set(); // 创建一个空的 Set
const anotherSet = new Set([1, 2, 3, 4, 5]); // 创建一个包含一些元素的 Set

2. 添加元素:

使用 add() 方法可以向 Set 中添加元素。如果元素已经存在,Set 会自动忽略,不会报错。

mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(1); // 添加重复元素,会被忽略

console.log(mySet); // 输出: Set(3) {1, 2, 3}

3. 删除元素:

使用 delete() 方法可以删除 Set 中的元素。如果元素不存在,也不会报错。

mySet.delete(2);

console.log(mySet); // 输出: Set(2) {1, 3}

4. 检查元素是否存在:

使用 has() 方法可以检查 Set 中是否存在某个元素。

console.log(mySet.has(1)); // 输出: true
console.log(mySet.has(2)); // 输出: false

5. 清空 Set:

使用 clear() 方法可以清空 Set 中的所有元素。

mySet.clear();

console.log(mySet); // 输出: Set(0) {}

6. 获取 Set 的大小:

使用 size 属性可以获取 Set 中元素的个数。

const mySet = new Set([1, 2, 3, 4, 5]);
console.log(mySet.size); // 输出: 5

Set 的进阶玩法:集合操作

Set 不仅仅可以用来去重,还可以进行一些有趣的集合操作,比如并集、交集、差集等等。

  • 并集 (Union): 将两个 Set 合并成一个,包含所有元素。
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);

const union = new Set([...setA, ...setB]); // 使用扩展运算符展开 Set
console.log(union); // 输出: Set(5) {1, 2, 3, 4, 5}
  • 交集 (Intersection): 找出两个 Set 中共同的元素。
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);

const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log(intersection); // 输出: Set(1) {3}
  • 差集 (Difference): 找出只存在于一个 Set 中,而不存在于另一个 Set 中的元素。
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);

// A - B
const differenceAB = new Set([...setA].filter(x => !setB.has(x)));
console.log(differenceAB); // 输出: Set(2) {1, 2}

// B - A
const differenceBA = new Set([...setB].filter(x => !setA.has(x)));
console.log(differenceBA); // 输出: Set(2) {4, 5}

Set 的应用场景:

  • 数组去重: 这是 Set 最常见的应用场景,也是它最擅长的事情。
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [...new Set(arr)]; // 使用扩展运算符将 Set 转换成数组
console.log(uniqueArr); // 输出: [1, 2, 3, 4, 5]
  • 跟踪访问过的 URL: 可以使用 Set 来记录用户访问过的 URL,避免重复访问。
const visitedUrls = new Set();

function visitUrl(url) {
  if (visitedUrls.has(url)) {
    console.log(`已经访问过 URL: ${url}`);
    return;
  }

  console.log(`正在访问 URL: ${url}`);
  visitedUrls.add(url);
}

visitUrl('https://www.example.com');
visitUrl('https://www.google.com');
visitUrl('https://www.example.com'); // 提示已经访问过
  • 数据验证: 可以使用 Set 来验证数据的唯一性,例如用户名、邮箱地址等。

Map:灵活的键值对,想放啥就放啥

Map 是一种存储键值对的数据结构,它与对象非常相似,但 Map 的键可以是任何数据类型,而对象的键只能是字符串或 Symbol。这就像一个超级灵活的字典,你可以用任何东西来索引你的信息,甚至可以用一幅画,一首歌,或者一个深奥的哲学概念!

1. 创建 Map:

创建一个 Map 也非常简单,只需要使用 new Map() 就可以了。

const myMap = new Map(); // 创建一个空的 Map
const anotherMap = new Map([
  ['name', 'John'],
  ['age', 30],
  [true, 'isMale']
]); // 创建一个包含一些键值对的 Map

2. 设置键值对:

使用 set() 方法可以向 Map 中添加键值对。

myMap.set('name', 'Alice');
myMap.set('age', 25);
myMap.set(true, 'isFemale');

console.log(myMap); // 输出: Map(3) {"name" => "Alice", "age" => 25, true => "isFemale"}

3. 获取值:

使用 get() 方法可以根据键获取值。如果键不存在,则返回 undefined

console.log(myMap.get('name')); // 输出: Alice
console.log(myMap.get('age')); // 输出: 25
console.log(myMap.get('city')); // 输出: undefined

4. 删除键值对:

使用 delete() 方法可以删除 Map 中指定键的键值对。

myMap.delete('age');

console.log(myMap); // 输出: Map(2) {"name" => "Alice", true => "isFemale"}

5. 检查键是否存在:

使用 has() 方法可以检查 Map 中是否存在指定键。

console.log(myMap.has('name')); // 输出: true
console.log(myMap.has('age')); // 输出: false

6. 清空 Map:

使用 clear() 方法可以清空 Map 中的所有键值对。

myMap.clear();

console.log(myMap); // 输出: Map(0) {}

7. 获取 Map 的大小:

使用 size 属性可以获取 Map 中键值对的个数。

const myMap = new Map([
  ['name', 'John'],
  ['age', 30]
]);
console.log(myMap.size); // 输出: 2

Map 的进阶玩法:遍历 Map

Map 提供了多种遍历方式,可以方便地访问 Map 中的键和值。

  • 使用 for...of 循环: 可以直接遍历 Map 中的键值对,每个键值对都是一个数组,包含键和值。
const myMap = new Map([
  ['name', 'John'],
  ['age', 30]
]);

for (const [key, value] of myMap) {
  console.log(`Key: ${key}, Value: ${value}`);
}
// 输出:
// Key: name, Value: John
// Key: age, Value: 30
  • 使用 forEach() 方法: 类似于数组的 forEach() 方法,可以遍历 Map 中的每个键值对。
const myMap = new Map([
  ['name', 'John'],
  ['age', 30]
]);

myMap.forEach((value, key) => {
  console.log(`Key: ${key}, Value: ${value}`);
});
// 输出:
// Key: name, Value: John
// Key: age, Value: 30
  • 使用 keys()values()entries() 方法: 可以分别获取 Map 中的所有键、所有值和所有键值对,然后使用 for...of 循环进行遍历。
const myMap = new Map([
  ['name', 'John'],
  ['age', 30]
]);

for (const key of myMap.keys()) {
  console.log(`Key: ${key}`);
}
// 输出:
// Key: name
// Key: age

for (const value of myMap.values()) {
  console.log(`Value: ${value}`);
}
// 输出:
// Value: John
// Value: 30

for (const entry of myMap.entries()) {
  console.log(`Entry: ${entry}`);
}
// 输出:
// Entry: name,John
// Entry: age,30

Map 的应用场景:

  • 存储 DOM 元素与数据的关联关系: 可以使用 Map 来存储 DOM 元素与相关数据的关联关系,例如用户 ID、状态等。
const element = document.getElementById('myElement');
const userData = { id: 123, name: 'Alice' };

const elementDataMap = new Map();
elementDataMap.set(element, userData);

console.log(elementDataMap.get(element)); // 输出: {id: 123, name: 'Alice'}
  • 缓存计算结果: 可以使用 Map 来缓存计算结果,避免重复计算。
const memoizedFactorial = new Map();

function factorial(n) {
  if (memoizedFactorial.has(n)) {
    console.log(`从缓存中获取 ${n}!`);
    return memoizedFactorial.get(n);
  }

  if (n === 0) {
    return 1;
  }

  const result = n * factorial(n - 1);
  memoizedFactorial.set(n, result);
  return result;
}

console.log(factorial(5)); // 输出: 120 (计算并缓存)
console.log(factorial(5)); // 输出: 120 (从缓存中获取)
  • 实现 LRU 缓存: LRU (Least Recently Used) 缓存是一种常用的缓存算法,可以使用 Map 来实现。

Set vs Map:选择困难症?

Set 和 Map 都是非常有用的数据结构,但它们的应用场景略有不同。

  • Set 专注于存储唯一的值,适用于去重、集合操作等场景。 把它想象成一个只能存放不同玩具的玩具箱。
  • Map 专注于存储键值对,适用于需要使用任意数据类型作为键的场景。 把它想象成一个可以贴标签的收纳盒,标签可以是任何东西。

如果你只需要存储一组唯一的值,那么 Set 是更好的选择。如果你需要存储键值对,并且键可以是任何数据类型,那么 Map 是更好的选择。

总结:

Set 和 Map 是 JavaScript 中非常有用的新集合类型,它们提供了更灵活、更高效的数据处理方式。学会使用它们,可以让你写出更优雅、更强大的代码。下次当你需要处理数据时,不妨试试 Set 和 Map,也许它们能给你带来意想不到的惊喜!

希望这篇文章能够帮助你更好地理解 Set 和 Map,并能够在你的日常开发中使用它们。记住,学习新的东西就像打开新的玩具盒,充满了乐趣和惊喜! 玩得开心!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注