Map 应用场景的真相居然是 ...

简介: Map 应用场景的真相居然是

当我准备集中精力完成《React 知命境》的时候,一个不起眼的小问题,幽幽的复现在我的眼前。

image.png

我的第一反应:这么简单的问题,想要用的时候就用呗。


但是... ... 不对,在我模糊的印象中,这个人能力挺强,常年看我的文章,对基础的研究比较深入,不会问那么随便的问题,这样敷衍之后,她肯定还有下一个问题等着我,我得回答专业一点,稳住我十年老油子的高大人设才行。


根据我的预判,她一定是想问什么时候用 Object,什么时候用 Map,在 JavaScript 的使用者中,有此疑问的不在少数。于是我就开始在记忆中搜索关于这两个对象的区别。


Object 和 Map 都可以存储键值对。


Object 的 key 值只能是数字、字符串,symbol。Map 的key值可以是任意数据类型。


Map 是可迭代对象,Object 不可以迭代。


Map 会记录属性的写入顺序,Object 不会记录写入的先后顺序甚至还有可能会排序。


Map 有 size 属性,而 Object 没有。


那什么时候用 Map 比 Object 更合适呢?


额... ...  糟糕,想不起来!


CPU 单线程运转了五分钟,无果。不过没关系,我还有万能的百度,输入关键字,开始搜索,很快就能得到答案。

image.png

果然百度大法好,搜索结果里有大量的文章在分析他们,头一条是掘金的一篇译文,一看就比较靠谱,于是满怀期待的点进去,开始阅读这篇文章。

image.png

文章在分析了大量的各自的特点之后,终于看到了我想要的内容,Map 的应用场景。


然而读着读着,好像有点不太对劲。更多的还是一些特性的分析,例如 Map 的删除操作性能更好,存储大数更合适,并没有介绍任何具体的场景。


而且还出现了一些我不太认同的小疑问。

image.png

好吧,这篇文章没有我找到的答案,如法炮制,我开始阅读别的文章。


然而... 大多数都是雷同的信息。


我依稀记得之前使用 Map 解决过一个非常棒的案例,可就是想不起来,加上搜索无望,我不免有些心急。


时间已经不知不觉过去了 10 分钟,我还没有回复那个尊称我为“波神”的小妹妹。我已经感受到了,我的大佬人设正在逐渐崩塌。


对哦,我可太蠢了,既然我之前使用过,把项目代码拿出来搜索一下不就找到了吗?


看到了希望的曙光,我进入了修改紧急 bug 的高效状态。迅速打开 vs code,打开我的项目代码,全局搜索 new Map。


哈哈,果然找到了大量使用 Map 的代码。

image.png

可是当我点开代码之后,才发现.... 居然没有熟悉感。全都不是我想要的场景。


这样的情况,用 {} 不更简单吗?

image.png

这肯定不是我写的代码。


真正的大师,永远怀着一颗学徒的心。


我实在想不起来以前那个非常适合用 Map 的场景了,搜索找不到,代码也找不到,好在我还有几个大厂大佬众多,且非常活跃的技术群。


于是我问.

image.png

我再问.

image.png

这个问题在几个群都引发了激烈的讨论,我就像一个小白疯狂的吸收着大佬们的知识。大佬之间的讨论就是不一样,很快我们就撇开了毫无意义的表面特性,开始聊起了性能。


有个字节的大佬抛出一个观点,他说,对象的读取没有 Map 快,所以他几乎都是能用 Map 的地方就会用 Map。


另一个大佬认为在速度上 Map 比 Object 并没有明显优势,在删除属性时 Map 表现更好一点。


我又想到了刚才看的掘金的文章,说是 Object 的读写速度更快,几个结论说法不一,于是讨论陷入了验证阶段。


如果这个事情能够得到论证的话,那么「能用 Map 的地方就使用 Map」 就是一个非常完美的答案。


为了证明这个事情,我开始考虑一个事情,Object 在内存中到底是如何存储的?Map 又是如何存储的呢?


依稀记得 V8 对 Object 的处理是有优化手段的,但是年代久远记不清晰了,于是有了新的方向,我再次踏上了寻找资料的征途:祭出百度。

image.png

果然不出我所料。


V8 对对象属性的存储结构并没有表面上那么简单,有特殊的处理。


在掘金和知乎的文章里,我找到这个图。

image.png

一个对象里有快属性和慢属性的区分。当属性数量较少时「in-object properties」,或者 key 为数字类型「elements」,都会采用快属性,快属性使用线性结构存储。所以读取属性的速度是非常快的。当属性变多,为了确保新增和删除的效率,此时会启用慢属性「properties」,采用词典键值对的方式存储属性内容。


我知道,很多人看到这里,肯定会疑问什么是线性结构,什么是非线性结构,哈哈哈,还好我没有疑问。


也就是说,从基础理论上来看,Object 会因为属性数量上分为两个阶段,从而解决 Object 的读写问题,而且,V8 还为 Object 创建了隐藏类用于记录每个属性的偏移量,也就意味着,Object 的读写不会太慢。


这个界限有的文章说是10个,但是我使用开发者工具的 Memery 记录的 snapshot 验证的结果不是这样,我也没有验证出来上限是多少


那么 Map 的存储在内存中又是什么结构呢?


哈哈,这个我知道!


散列表 + 链表 + 红黑树 = hashMap。


我突然就悟了。


也就是说,如果属性数量偏小的情况下,读写速度上,Object 和 Map 应该不会有太大的差别。而只有数据量非常大的时候,才会逐渐体现出来差别。那么这个数据量大,到底要达到什么程度呢?我也不确定。


写个案例,验证一下。


首先验证一下写入的时间成本。

// 先定义一个数量上限
const up = 9999
var mt1 = performance.now()
var map = new Map()
for(var i = 0; i < up; i++) {
  map.set(`f${i}`, {a: i, children: { a: i }})
}
console.log(`   Map: `, performance.now() - mt1)
var ot1 = performance.now()
var obj = {}
for (var i = 0; i < up; i++) {
  obj[`f${i}`] = {b: i, children: {a: i}}
}
console.log('Object: ', performance.now() - ot1)

刷新了 20 多次,基本上都在 5 ~ 10 ms 之间波动。时间上谁高谁低没有明确的差别。不过 Object 比 Map 耗时更短的次数会多一点。这符合我的预期。

image.png

image.png

接下来我逐渐调高 up 变量的值。继续验证。当我把 up 的值设置为 99999 时,耗时上依然没有明显的差别。

image.png

当我把 up 的值设置为 499999 时,Object 的写入速度才开始稳定的比 Map 耗时更长。

image.png

好家伙,我从来不会维护这么大的数据量在项目中。


接下来,我又依次验证了读取速度和删除速度。


在删除上,我把 up 的值设置为 199999 ,Object 的删除耗时才会稳定比 Map 慢。

// 删除
for(var i = 0; i < up; i++) {
  map.delete(`f${i}`)
}

image.png

在读取速度上,up 的值为 159999 时 Object 的读取速度会稳定比 Map 慢。

// 访问
for (var i = 0; i < up; i++) {
  map.get(`f${i}`)
}

所以,在性能的表现上,新增、删除、读取的速度,在数量非常少时,Object 的表现可能会稍微好那么一点点点,甚至不太明显能感知得出来。


而在数量非常大的时候,Map 的表现会比 Object 好。可是这种程度的数量,我想很难在项目中把数据维护到这种程度。


验证结果让我居然神奇的发现,上面两位大佬不一样的观点,居然都说的过去。


能用 Map 就用 Map ,没什么毛病。Object 也没有比 Map 有什么明显的速度优势。


当我做完验证回过头来看群消息的时候,另外一个群的大佬提供了一个非常牛逼的应用场景。那就是策略模式的封装。


膜拜!


策略模式通常情况下都是一个键值对应一个规则。但是!在某些特殊场景下,会出现多个键值对应一个规则的情况。这个时候,Map 就有了用武之地。Map 支持正则表达式作为 key 值,这样,使用 Map 就可以存储多对一的匹配规则。

image.png

折腾了一天,我苦逼的发现,我终于想起来我之前用 Map 实现的那个应用场景是什么了。


那就是聊天列表和聊天内容的实现。


聊天列表与聊天内容因为要缓存很多信息,数据量够大,并且,聊天是一个频繁变动的场景。聊天列表有新消息就会重新排序,聊天内容也会频繁的插入新的消息,特别是群聊,对聊天内容的顺序也有严格的要求。


数据量大、频繁写入、排序、对写入顺序有严格的要求,这个场景就是为 Map 量身定做的一样。

相关文章
|
6月前
|
存储 数据格式
Set和Map的应用场景
Set和Map的应用场景
|
4月前
|
JSON JavaScript API
JS【详解】Map (含Map 和 Object 的区别,Map 的常用 API,Map与Object 的性能对比,Map 的应用场景和不适合的使用场景)
JS【详解】Map (含Map 和 Object 的区别,Map 的常用 API,Map与Object 的性能对比,Map 的应用场景和不适合的使用场景)
97 0
数组map方法的应用场景及经典面试题
数组map方法的应用场景及经典面试题
139 0
|
存储 自然语言处理 JavaScript
Map 应用场景的真相居然是 ...
我的第一反应:这么简单的问题,想要用的时候就用呗。 但是... ... 不对,在我模糊的印象中,这个人能力挺强,常年看我的文章,对基础的研究比较深入,不会问那么随便的问题,这样敷衍之后,她肯定还有下一个问题等着我,我得回答专业一点,稳住我十年老油子的高大人设才行。 根据我的预判,她一定是想问什么时候用 Object,什么时候用 Map,在 JavaScript 的使用者中,有此疑问的不在少数。于是我就开始在记忆中搜索关于这两个对象的区别。 Object 和 Map 都可以存储键值对。
360 0
Map 应用场景的真相居然是 ...
|
5月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
|
2月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
2月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
1月前
|
存储 分布式计算 Java
Stream很好,Map很酷,但答应我别用toMap():Java开发中的高效集合操作
在Java的世界里,Stream API和Map集合无疑是两大强大的工具,它们极大地简化了数据处理和集合操作的复杂度。然而,在享受这些便利的同时,我们也应当警惕一些潜在的陷阱,尤其是当Stream与Map结合使用时。本文将深入探讨Stream与Map的优雅用法,并特别指出在使用toMap()方法时需要注意的问题,旨在帮助大家在工作中更高效、更安全地使用这些技术。
42 0
|
3月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
3月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。