面试管:来,小卡拉米,来个最基本的,说一下Set和Map的理解

听到这个题之后的内心独白:

卧槽,我都是背的高达上,为啥来个基本的,没准备呀,这让我怎么吹牛逼,我特码的面的可是高级呀,不应该问我:项目调优、首屏优化、打包优化等问题吗?这个面试管不安套路出牌呀,完了,兜不住了,大爷的,嘎了,凉了,gg了,我高大上的八股文白背了,哎,只能江湖下次在见了!

Set和Map

概念说明

我们想一个问题,ES6中为啥要新增Set和Map这两种数据结构呢?为啥类似于Java中都有Set和Map这两种数据结构,而且还有其变形。

官方回答(八股文答案,就是没毛病,感觉说了跟没说一样,呵呵):

总的来说,ES6中新增的Set和Map数据结构,主要是为了提供更强大和灵活的数据操作能力,以满足更复杂的编程需求。

什么是Set

Set我们可以叫做一种集合得数据结构

什么是Map

Map我们可以叫做一种字典得数据结构

那什么是集合?什么又是字典?

集合:是由一堆无序的、相关联的,而且不能重复的内存结构组成的组合

字典:是一些元素的集合。每个元素有一个称作key 的域,不同元素的key 各不相同

他们的共同点和不同点:

  • 共同点:集合、字典都可以存储不重复的值
  • 不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储

使用示例

set 初始化

  • 使用 new Set() 创建一个空 Set
  • 使用 new Set(iterable) 创建一个包含 iterable 中所有值的 Set

着重注意一下第二个new Set(iterable)的这种形式,iterable是指可迭代的。

javascript 复制代码
// 初始化一个set数据结构
const set1 = new Set(); // 创建一个空set
const set2 =  new Set([1,2,3]); // 创建一个包含三个值的set
const set3 = new Set('abc'); // 创建一个包含三个字符的 Set
const set4 = new Set(new Map([['a', 1], ['b', 2]])); // 创建一个包含两个键值对的 Set

Set的用法

只要谈起数据结构,最基本的操作就是增删改查,Set相关常用的API如下:

  • add: 添加某个值(别乱想,添加只有各个方法,可别乱联想数组的push,那你可能数组用多了)
  • delete: 删除某个值
  • has:返回一个布尔值,表明是否含有这个值
  • clear:清空所有的成员

上面增删改查的API没啥好说的。

Set的大小或者说成员的个数是size属性,你可别来个length,length是数组的长度。

数据存在完了,我们要干啥呢?自然就是遍历数据:

Set提供的遍历方法

  • keys: 返回键名的遍历器
  • values: 返回键值的遍历器
  • entries:返回键值对的遍历器
  • forEach:使用回调函数遍历每个成员 (别大惊小怪,用forEach遍历数组,怎么人家Set就不能有)
  • for...of: 迭代Set中所有的成员

稍微发泄一下:

不行,这个得上示例,八股文背诵得多了,有点串了,不上示例,印象不深刻呀,干就完了,大爷的。

特娘的,现在这个吊环境,你不上点花活,面试管都记不住你,关键是我特妈的基本的API都背串了,还上花活,

很是苦逼。我现在用AIGC编程,可惜面试的时候不让我用呀,哈哈。

操,想转行,一看别的行业也是遍地尸体,回头一想,要是转行了那我前期的积累(没啥价值,还不舍得扔的 积累)不就都废了,

妈的,算了,继续跟你们卷,我要一天背10个八股文,卷死你们,哈哈。

遇到点挫折,就特妈的大环境不好,怎么你到哪,那就大环境不好了,哈哈,要想PUA别人就得先PUA自己,我要把我自己PUA死,要不怎么撑下去和你们卷。

他妈的,总之,干就完了。

javascript 复制代码
// 初始化一个set数据结构
const set1 = new Set(); // 创建一个空set
const set2 =  new Set([1,2,3]); // 创建一个包含三个值的set
const set3 = new Set('abc'); // 创建一个包含三个字符的 Set
const set4 = new Set(new Map([['a', 1], ['b', 2]])); // 创建一个包含两个键值对的 Set

// 一般来说比较常用的是values, entries,forEach, for...of方法

// forEach
set2.forEach(value => { console.log(value) });

//  for...of
for (const value of set2) { console.log(value) }

// values:  注意返回值是个遍历器,要遍历期中的数据才可以用
const iterator1 = set3.values();
for (const value of iterator1) { console.log(value) }

// entries:  返回的也是迭代器,只是迭代器每一项的值是数组[index, value]
const iterator2 = set2.entries();
for (const entry of iterator2) {console.log(entry) }

Map初始化

Map类型是键值对的有序列表,而键和值都可以是任意类型(包括原始值和对象),Map本身是一个构造函数,用来生成Map数据结构(我就问一下子,我八股文背的怎么样,服不服)。

Map的特性:

  • 键值对: Map 中的每个元素都是一个键值对,键可以是任何类型,值可以是任何类型。
  • 无序:Map 中的元素是无序的,插入顺序不会影响迭代顺序。
  • 唯一键:Map 中的键是唯一的,不允许重复。
  • 高效:Map 在查找、插入和删除操作方面都非常高效。

创建Map:

可以使用 new Map() 创建一个空 Map,也可以使用 new Map(iterable) 创建一个包含 iterable 中所有键值对的 Map。

javascript 复制代码
const map1 = new Map(); // 创建一个空 Map
const map2 = new Map([['a', 1], ['b', 2]]); // 创建一个包含两个键值对的 Map

// 这个有点意思,注意看,还不抓紧复制到控制台看看,等啥呢,大兄弟
const map3 = new Map(new Set([['a', 1], ['b', 2]])); // 创建一个包含两个键值对的 Map

// 哈哈,其实map2和map3创建是一样的,看我花活玩的怎么样。

Map的用法

继续轮我三板斧,来,上增删改查,不行,说增删改查有点low,这把换个洋气点的,CRUD,怎么样?是不是跟国际接轨了。

  • 添加元素:大哥他不可是add哈,add可是人家Set的,它的是set,对的,使用 map.set(key, value) 添加一个键值对到 Map 中
  • 删除元素:使用 map.delete(key) 删除一个键值对从 Map 中。
  • 检查元素是否存在:使用 map.has(key) 检查 Map 中是否包含某个键。
  • 获取元素:使用 map.get(key) 获取某个键对应的值。大哥,map有这个get方法,Set可没有哈,记清楚了

map的长度跟Set一样,也是size属性,表示元素集合的个数。

数据存储完了,是不是要上遍历了,搞起来:

  • 使用 forEach 迭代 Map 中的所有键值对
  • 使用 for...of 迭代 Map 中的所有键值对
  • 使用 keys 方法获取 Map 中所有键的迭代器
  • 使用 values 方法获取 Map 中所有值的迭代器
  • 使用 entries 方法获取 Map 中所有键值对的迭代器
javascript 复制代码
const map1 = new Map(); // 创建一个空 Map
const map2 = new Map([['a', 1], ['b', 2]]); // 创建一个包含两个键值对的 Map
const map3 = new Map(new Set([['a', 1], ['b', 2]])); // 创建一个包含两个键值对的 Map

// forEach,遍历键值对哈!三遍:键值对
map1.forEach((value, key) => { console.log(key, value)} );

// 同上
for (const [key, value] of map2) {console.log(key, value)}

// 所有的键
const keys = map3.keys();
for (const key of keys) {console.log(key) }

// 所有的值
const values = map3.values();
for (const value of values) { console.log(value) }

// 键值对
const entries = map3.entries();
for (const [key, value] of entries) { console.log(key, value) }

不行拿到控制台多输几遍,没啥丢人的,不满你说,我打了七八遍,哈哈,是不是很有天赋,仅仅七八遍就记住了一半,小卡拉米们,你们已经输在起跑线了,输在天赋上了,哈哈!

Set常见用法

去重

Set数据结构中不允许重复的元素,利用这个特性可以很简洁的实现数组去重

javascript 复制代码
[...new Set([1,2,3,3,1,2])]

不考虑性能的情况下,Set去重可以做到相当的简洁。

交集

可以使用Set计算两个数组的交集,写法也是相当的简洁。

javascript 复制代码
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);

// 交集
const intersection = new Set([...set1].filter(x => set2.has(x)));

并集

javascript 复制代码
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);

// 并集
const union = new Set([...set1, ...set2]);

差集

javascript 复制代码
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);

// 差集
const difference = new Set([...set1].filter(x => !set2.has(x)));

相等

判断两个set是否相等

javascript 复制代码
const set1 = new Set([1, 2, 3]);
const set2 = new Set([1, 2, 3]);

// 方法一:比较两个 Set 的大小和元素
console.log(set1.size === set2.size && [...set1].every(x => set2.has(x))); // true

// 方法二:使用 Set 的 `equals` 方法
console.log(set1.equals(set2)); // true

来放松一下:

在惊叹Set和Map中,喊着一声声牛逼,在牛逼中逐渐的迷失自己,感觉自己又可以了,哈哈,这是错觉,小卡拉米!特妈的,干就完了!小卡拉米冲吧!

哈拉西大,娘希匹,特奶奶的上Map!

Map常见用法

转换

怎么说呢,其实Map的转换是很常见的用法,一定要掌握的。

  • Map 转为数组 :使用 [...map]Array.from(map) 将 Map 转换为数组。
  • 数组转为 Map :使用 new Map(iterable) 将数组转换为 Map。
  • Map 转为对象 :使用 Object.fromEntries(map) 将 Map 转换为对象。
  • 对象转为 Map :使用 new Map(Object.entries(obj)) 将对象转换为 Map。
javascript 复制代码
// map转换为数组
const arr = [...map1]; // [["a", 1]]
// 数组转换map
const map4 = new Map([['a', 1], ['b', 2]]);
// Map转换对象(这个有点意思哈,记住了,有用的)
const obj = Object.fromEntries(map4); // {a: 1, b: 2}
// 对象转换map
const map5 = new Map(Object.entries(obj)); // Map {a => 1, b => 2}

模拟字典

可以使用 Map 来模拟字典,键可以是单词,值可以是单词的定义。

javascript 复制代码
const dictionary = new Map([
  ["apple", "一种水果"],
  ["banana", "一种水果"],
  ["cat", "一种动物"],
]);

console.log(dictionary.get("apple")); // "一种水果"

缓存数据

这个有点意思,可以使用 Map 来缓存数据,提高性能。

javascript 复制代码
const cache = new Map();

function getData(key) {
  if (cache.has(key)) {
    return cache.get(key);
  } else {
    const data = fetch(key);
    cache.set(key, data);
    return data;
  }
}

const data = getData("https://www.example.com");

对象去重

可以使用 Map 来对对象进行去重,例如只保留对象的 id 属性

javascript 复制代码
const objects = [
  { id: 1, name: "John" },
  { id: 2, name: "Mary" },
  { id: 1, name: "Alice" },
];

const uniqueObjects = new Map();

for (const object of objects) {
  if (!uniqueObjects.has(object.id)) {
    uniqueObjects.set(object.id, object);
  }
}

console.log(uniqueObjects.values()); // [ { id: 1, name: "John" }, { id: 2, name: "Mary" } ]

统计元素次数

可以使用 Map 来统计元素出现的次数。

javascript 复制代码
const words = ["a", "b", "c", "a", "b"];
const wordCounts = new Map();

for (const word of words) {
  if (wordCounts.has(word)) {
    wordCounts.set(word, wordCounts.get(word) + 1);
  } else {
    wordCounts.set(word, 1);
  }
}

// 上面的写法可以玩花活的,你懂的,娘希匹,反正我就觉得这种写法最好,可读性好高。(翻译成人话就是:我太菜了,这个才刚看明白,还花活,绕了我吧)

console.log(wordCounts); // Map { "a" => 2, "b" => 2, "c" => 1 }

实现 LRU 缓存

可以使用 Map 来实现 LRU 缓存,即最近最少使用缓存。

我擦,都别拦我,我感觉我又行了,我可以去搞算法了,啥动态规划、回溯、贪心等等,都不在话下,就是主打一个看不懂,

等我去召唤法师,卷死你们,哈哈。

javascript 复制代码
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
  }

  get(key) {
    if (this.cache.has(key)) {
      const value = this.cache.get(key);
      this.cache.delete(key);
      this.cache.set(key, value);
      return value;
    } else {
      return undefined;
    }
  }

  set(key, value) {
    if (this.cache.size === this.capacity) {
      const oldestKey = this.cache.keys().next().value;
      this.cache.delete(oldestKey);
    }
    this.cache.set(key, value);
  }
}

const cache = new LRUCache(2);

cache.set("a", 1);
cache.set("b", 2);

console.log(cache.get("a")); // 1
cache.set("c", 3);

console.log(cache.get("b")); // undefined

扩展

来吧,都说了Set,那不得介绍一下WeakSet,要不怎么显得我花活多(人话是:哎,真他妈的学够了,他们天天更新,我特码的天天跟个菜鸟一样,学学学,时候是个头呀,毁灭吧!)

WeakSet 简介

WeakSet 是 JavaScript 中一种新的数据结构,它用来存储弱引用的集合。与传统的 Set 数据结构不同,WeakSet 中的对象不会阻止垃圾回收机制回收它们。

特性:

  • 弱引用:WeakSet 中的对象是弱引用,这意味着垃圾回收机制可以回收它们,即使它们仍然被 WeakSet 引用。
  • 无序:WeakSet 中的元素是无序的,插入顺序不会影响迭代顺序。
  • 唯一键:WeakSet 中的键是唯一的,不允许重复。
  • 高效:WeakSet 在查找、插入和删除操作方面都非常高效。

WeakMap简介

但用于存储键值对。WeakMap 中的键是弱引用,这意味着垃圾回收机制可以回收它们,即使它们仍然被 WeakMap 引用。

特性:

  • 弱键:WeakMap 中的键是弱引用,这意味着垃圾回收机制可以回收它们,即使它们仍然被 WeakMap 引用。
  • 无序:WeakMap 中的元素是无序的,插入顺序不会影响迭代顺序。
  • 唯一键:WeakMap 中的键是唯一的,不允许重复。
  • 高效:WeakMap 在查找、插入和删除操作方面都非常高效。

等我后续补充WeakSet 和 WeakMap ,我这小卡拉米和你们后边继续交流学习...

总结

基础不过关,都是几把扯淡,牛逼吹的越狠,可能越兜不住。

面试管:下一个问题(小卡拉米,说这么多有啥用,我们只要个性价比高的,能做的了项目即可,你跟我扯这么多,扯这么多高达上的干啥,一句没说到点子上,净浪费时间,你不还是来面外包了,小样)。

面试管心理独白:这个小卡拉米还想隔着跟我装逼,坚决不能惯着他,等我搜个高级面试在来,看我不问死他,小样,打击不死他,娘希匹!

中间内心独白和旁白纯属编造,你的懂得。

哈喽,brother,欢迎来到《新世界》,我可是电梯战神,不给我点赞加收藏,我拿刀嘎你!

相关推荐
Nan_Shu_6141 分钟前
学习: Threejs (2)
前端·javascript·学习
G_G#9 分钟前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界25 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路33 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug37 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213839 分钟前
React面向组件编程
开发语言·前端·javascript
学历真的很重要39 分钟前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
持续升级打怪中1 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架