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

简介: 我的第一反应:这么简单的问题,想要用的时候就用呗。但是... ... 不对,在我模糊的印象中,这个人能力挺强,常年看我的文章,对基础的研究比较深入,不会问那么随便的问题,这样敷衍之后,她肯定还有下一个问题等着我,我得回答专业一点,稳住我十年老油子的高大人设才行。根据我的预判,她一定是想问什么时候用 Object,什么时候用 Map,在 JavaScript 的使用者中,有此疑问的不在少数。于是我就开始在记忆中搜索关于这两个对象的区别。Object 和 Map 都可以存储键值对。

微信图片_20220511155726.png


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


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


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

ObjectMap 都可以存储键值对。


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


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


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

Map 有 size 属性,而 Object 没有。


那什么时候用 MapObject 更合适呢?


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


微信图片_20220511155731.png


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


微信图片_20220511155735.png


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


微信图片_20220511155739.png


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


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


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


微信图片_20220511155739.png

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


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


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


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


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


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


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


微信图片_20220511155747.png

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


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


微信图片_20220511155750.png


这肯定不是我写的代码。


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


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


于是我问.


微信图片_20220511155753.jpg


我再问.


微信图片_20220511155757.jpg


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


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


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


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


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


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


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


微信图片_20220511155800.png


果然不出我所料。


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


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


微信图片_20220511155804.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 耗时更短的次数会多一点。这符合我的预期。


微信图片_20220511155807.png

微信图片_20220511155811.png


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


微信图片_20220511155814.png


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


微信图片_20220511155817.png


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


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


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


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


微信图片_20220511155820.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 就可以存储多对一的匹配规则。

相关文章
|
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
|
6月前
|
存储 缓存 自然语言处理
Map 应用场景的真相居然是 ...
Map 应用场景的真相居然是
数组map方法的应用场景及经典面试题
数组map方法的应用场景及经典面试题
139 0
|
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是一个不允许重复元素的集合。