当前位置:首页 > 文章 > 正文内容

面试高频的ES6中的Map和Set数据结构详解

廖万里1年前 (2022-11-06)文章83565

面试高频的ES6中的Map和Set数据结构详解

Map

ES6之前我们需要存储键值对的集合时,只能使用对象作为映射,但对象作为映射的主要缺点是不能使用非字符串值作为键

看看下面例子:

const obj  = { name:'yxz'},       obj1 = {name:'yzz'}  const res = {}  res[obj] = 'hello' res[obj1] = 'hey'  res[obj]   // 'hey' res[obj1]  // 'hey' 复制代码

因为obj和ojb1两个对象字符串化都是[object Object],所以res对象相当于设置了一个key,而后面的赋值把前面的覆盖掉了。

而ES6新增了一个数据结构map(...),它的key和传统对象的key的区别是:map(...)的key可以是任意类型的,而传统对象的key只能是字符串类型。

用法
const obj = {    name:'刘德华' }  const obj1 ={    name:'黎明' }  const m = new Map()  // 设置一对值 m.set(obj,'ldh') m.set(obj1,'lm')  // 读取值 m.get(obj)  // 'ldh' m.get(obj1) // 'lm' 复制代码

注意这里不能像对象那样使用 [] 来获取key的值,但同等可以使用get(...) 方法来获取。

从map中删除一个元素

需要从map中删除一个元素,不能像对象那样使用delete操作符,取而代之的是delete(...)方法。

const x = {   name:'张学友' }  const y = {   name:'周星驰' }  const m = new Map()  // 设置一对值 m.set(x,'zxy') m.set(y,'zxc') // 集合里元素个数 m.size. // 2 // 删除一个元素 m.delete(x)    // 集合里元素个数 m.size  // 1 复制代码
清空map中的所有元素
const x = {   name:'张学友' }  const y = {   name:'周星驰' }  const m = new Map()  // 设置一对值 m.set(x,'zxy') m.set(y,'zxc')  m.size.  // 2 m.clear() m.size.  // 0 复制代码

map(...)构造器也可以接受一个iterable,这个迭代器必须产生一列数组,每个数组的第一个元素是键(key),而第二个元素是值(value)。跟entries(...)方法产生的形式是完全一样的。

const m = new Map()  const x = {   name:'张学友' }  const y = {   name:'周星驰' }  const m2 = new Map( m.entries() ) // 等价于以下 const m3 = new Map( m ) 复制代码

因为map的实例是一个iterable,它的默认迭代器与entries()相同,所以更推荐使用后面的简写方式。

手动指定map的项目
const x = {   name:'张学友' }  const y = {   name:'周星驰' }  const m = new Map([   [x,'zxy'],   [y,'zxc'] ])  m.get(x)  // zxy m.get(y)  // zxc 复制代码
map值

要从map中得到一个值,可以使用values(...)方法,它会返回一个迭代器。

const m = new Map()  const x = {   name:'张学友' }  const y = {   name:'周星驰' }  m.set(x,'zxy') m.set(y,'zxc')  const vals = [ ...m.values() ]  vals  // ['zxy','zxc'] Array.from( m.values() )  // ['zxy','zxc']  vals[0][0] === x  // true vlas[0][1]   // 'zxy'  vals[1][0] === y  // true vlas[1][1]   // 'zxc' 复制代码
Map键

需要得到一列键,可以使用keys(...),它会返回map中键上的迭代器。

const m = new Map()  const x = {   name:'张学友' }  const y = {   name:'周星驰' }  m.set(x,'zxy') m.set(y,'zxc')  const keys = [ ...m.keys() ]  keys[0] === x  // true keys[1] === y  // true 复制代码

需要确定一个map中是否有给定的键,可以使用has(...)方法

cosnt m = new Map()  const x = {   name:'发哥' }  const y = {   name:'周星驰' }  m.set(x,'cool')  m.has(x)   // true m.has(y)   // false 复制代码

map的本质是允许你把某些额外的信息(值)关联到一个对象(键)上,而无需把这个信息放到对象中。对于map来说,尽管可以使用任意类型的值作为键,但通常我们只会使用对象,因为字符串或者其他基本类型已经可以作为普通对象的键来使用了。也就是说,除非某些或者全部键需要是对象,否则可以继续使用普通对象来作为映射,这种情况下map才是更加合适的。

如果使用对象作为映射的键,这个对象后来被丢弃(所有的引用解除),试图让垃圾回收回收其内存,那么map本身仍然会保持其项目,也就是仍然引用其项目,不被垃圾回收掉。

利用两个平行数组来模拟map
const keys = [],       vlaues = []  const x = {id:1},       y = {id:2}  keys.push(x) values.push('id---1')  key.push(y) values.push('id---2')  key[0]  === x  // true key[1]  === y  // true  values[0] // 'id---1' values[1] // 'id---2' 复制代码

这样可以模拟实现伪map,但是这样做的缺点是:访问的时间复杂度不再是O(1),而是O(n)了。

WeakMap

前面说到如果使用对象作为映射的键,这个对象后来被丢弃(所有的引用解除),试图让垃圾回收回收其内存,那么map本身仍然会保持其项目,也就是仍然引用其项目。那么就可以使用WeakMap来解决这个问题。

WeakMap是map的变体,二者的多数外部行为特性是一样的,区别在于内部内存分配(垃圾回收)的工作方式。也可以说Map强引用,而WeakMap则是弱引用的。

WeakMap只接受对象作为键,这点与Map不同,Map既可以使用对象,也可以使用简单类型作为键。这些对象是被弱持有的,也就是说如果对象本身被垃圾回收的话,在WeakMap中的这个项目也会被移除。

Map类型,WeakMap的API说类似的,但是WeakMap没有clear()size方法/属性,也不会暴露任何键、值或项目上的迭代器。

const m = new WeakMap()  const x = {   id:'007' }  m.set(x,'凌凌漆')  m.has(x)  // true 复制代码

Map一样,通过weakMap可以把信息与一个对象软关联起来。而在对这个对象没有完全控制权的时候,这个功能特别有用!比如DOM元素。如果作为映射键的对象可以被删除,并支持垃圾回收,那么WeakMap就是合适人选了。

注意:WeakMap只是弱引用它的键,并不是值,这一点很重要。

const m = new WeakMap()  const x = {id:1},       y = {id:2}  m.set(x,y)  x = null  // 垃圾回收 y = null  // 没有垃圾回收 复制代码

Set

Set是一个值的集合,其中的值是唯一的(当重复会被忽略)。

Set的API和Map类似。只是add(...)方法替换了get(...),没有get(...)方法。

const s = new Set()  const x = {id:1},       y = {id:2}  s.add(x) s.add(y) s.add(x)  s.size. // 2  s.delte(y) s.size. // 1  s.clear() s.size  // 0 复制代码

Set(...)构造器形式和Map(...)类似,都可以接受一个iterable,比如另外一个Set或者仅仅是一个值的数组。与Map(...)接受一个项目(entry)列表(键/值数组的数组)不同,Set(...)接受的是值(vlaue)列表,也就是值的数组。

const x = {id:1},       y = {id:2}  const s = nwe Set([x,y]) 复制代码

Set(...)不需要get(...)是因为不会从集合中取一个值,而是使用has(...)判断一个值是否存在。

const x = {id:1},       y = {id:2}  const s = nwe Set([x])  s.has(x)  // true s.has(y)  // false 复制代码

除了会把-0和0当作是一样的而不加区别之外,has(...) 中的比较算法和Object.is(...) 几乎是一样的。

Set迭代器

Set的迭代器和Map一样。对于Set来说,二者行为特性不同,但它和Map迭代器的行为是对成的。

const s = new Set()  const x = {id:1},       y = {id:2}  s.add(x).add(y)  // 没错!支持链式调用  const keys = [ ...s.keys() ],       values = [ ...s.values() ],       entries = [ ...s.entries() ]  keys[0] === x  // true keys[1] === y  // true  values[0] === x  // true values[1] === y  // true  entries[0][0] === x  // true entries[0][1] === x  // true  entries[1][0] === y  // true entries[1][1] === y  // true 复制代码

keys(...)values(...)迭代器都从set中yieId出一列不重复的值。entries(...)迭代器yieId出一列项目数组,其中的数组的两个项目都是唯一set值。set的默认迭代器是它的values(...)迭代器。

Set固有的唯一性是它最有用的特性!

const s = new Set([1,2,3,1,'1',6])  const arr = [ ...s ]  arr   // [1,2,3,'1',6] 复制代码

set的唯一性不允许强制转换,也就是说1'1'是不同的值。

应用场景
  • Tabs:比如日常开发中,项目的Tabs头部页签,可以用Set来维护,因为它是唯一的,不会因为点击一样的页面出现相同的Tabs。

  • 数组去重

WeakSet

就像WeakMap弱引用它的键一样,WeakSet对其值也是弱引用的( Set没有键 )。

const s = new WeakSet()  const x = {id:1},       y = {id:2}  s.add(x).add(y)  x = null  // x可以被垃圾回收 y = null  // y可以被垃圾回收 复制代码

注意:WeakSet的值必须是对象,而并不像Set一样可以是原生类型值。

总结

  • Map是键-值对,其中的值不只是字符串/原生类型,也可以是对象。Set是成员值(任意类型)唯一的列表。

  • WeakMap也是Map,其中的键(对象)是弱引用的,因此当它是对这个对象的最后一个引用的时候,垃圾回收就可以回收这个项目了。WeakSet也是Set,其中的值是弱引用的,也就是说如果其中的项目是对这个对象最后一个引用的时候,就可以被垃圾回收掉。

地图

在ES6之前,当我们需要存储一组键-值对时,只能使用对象作为映射,但是使用对象作为映射的主要缺点是不能使用非字符串值作为键。

请看下面的例子:

const obj = { name:'yxz'},

obj1 = {name:'yzz'}


const res = {}


你好

res[obj1] = '嘿'


嘿,嘿

res[obj1] //'嘿'

复制代码

因为obj和ojb1对象都是[object Object],res对象相当于设置了一个键,后面的赋值会覆盖前面的。

ES6添加了新的数据结构映射(...).它的键与传统对象的键的区别在于...)可以是任意类型,而传统对象的key只能是string类型。

使用

const obj = {

姓名:“刘德华”

}


const obj1 ={

名称:“黎明”

}


const m =新地图()


//设置一对值

m.set(obj,' ldh ')

m.set(obj1,' lm ')


//读取值

m.get(obj) // 'ldh '

m.get(obj1) // 'lm '

复制代码

注意,这里不能像对象一样使用[]获取key的值,但是也可以使用get(...)的方法来得到它。

从地图中删除元素

您需要从地图中删除一个元素。您应该使用delete(...)方法代替。

常数x = {

姓名:“张学友”

}


常数y = {

名字:“周星驰”

}


const m =新地图()


//设置一对值

m.set(x,' zxy ')

m.set(y,' zxc ')

//集合中元素的数量

m.size. // 2

//删除元素

删除(x)

//集合中元素的数量

m.size // 1

复制代码

清空地图中的所有元素

常数x = {

姓名:“张学友”

}


常数y = {

名字:“周星驰”

}


const m =新地图()


//设置一对值

m.set(x,' zxy ')

m.set(y,' zxc ')


m.size. // 2

m.clear()

m.size. // 0

复制代码

地图(...)构造函数也可以接受iterable,它必须产生一个列数组。每个数组的第一个元素是一个键,第二个元素是一个值。由条目生成的表单(...)方法完全一样。

const m =新地图()


常数x = {

姓名:“张学友”

}


常数y = {

名字:“周星驰”

}


const m2 =新映射(m.entries())

//相当于以下内容

常数m3 =新地图(米)

复制代码

因为map的实例是一个iterable,它的默认迭代器和entries()一样,所以更推荐使用后者的缩写。

手动指定地图项目。

常数x = {

姓名:“张学友”

}


常数y = {

名字:“周星驰”

}


const m =新地图([

[x,' zxy'],

[y,' zxc']

])


m.get(x) // zxy

m.get(y) // zxc

复制代码

地图值

要从地图中获取值,您可以使用值(...)方法,它将返回一个迭代器。

const m =新地图()


常数x = {

姓名:“张学友”

}


常数y = {

名字:“周星驰”

}


m.set(x,' zxy ')

m.set(y,' zxc ')


常量值= [...m.values() ]


vals // ['zxy ',' zxc']

Array.from( m.values() ) // ['zxy ',' zxc']


vals[0][0] === x // true

vlas[0][1] // 'zxy '


vals[1][0] === y // true

vlas[1][1] // 'zxc '

复制代码

地图键

要获得键的列表,可以使用键(...),这将返回映射中键的迭代器。

const m =新地图()


常数x = {

姓名:“张学友”

}


常数y = {

名字:“周星驰”

}


m.set(x,' zxy ')

m.set(y,' zxc ')


常量键= [...m.keys() ]


keys[0] === x // true

keys[1] === y // true

复制代码

要确定给定的键是否存在于映射中,可以使用has(...)方法。

cosnt m =新地图()


常数x = {

姓名:“发哥”

}


常数y = {

名字:“周星驰”

}


m.set(x,'酷')


m.has(x) // true

m.has(y) // false

复制代码

map的本质在于,它允许您将一些额外的信息(值)关联到一个对象(键),而无需将这些信息放入对象中。对于map来说,虽然任何类型的值都可以作为键,但是通常我们只使用对象,因为字符串或者其他基本类型已经可以作为普通对象的键了。也就是说,除非部分或全部键需要是对象,否则可以继续使用普通对象作为映射,这种情况下map更合适。

如果对象被用作映射的键,并且这个对象后来被丢弃(所有引用被释放)以试图获得垃圾收集来恢复它的内存,那么映射本身将仍然保留它的项,即仍然引用它的项,并且不会被垃圾收集。

Map由两个平行阵列模拟。

const keys = [],

vlaues = []


const x = {id:1},

y = {id:2}


按键。按压(x)

values.push('id - 1 ')


按键(y)

values.push('id - 2 ')


key[0] === x // true

key[1] === y // true


值[0] // 'id - 1 '

值[1] // 'id - 2 '

复制代码

这样可以模拟伪映射,但是缺点是访问的时间复杂度不再是O(1),而是O(n)。

WeakMap

如前所述,如果一个对象被用作映射的键,并且这个对象后来被丢弃(所有引用都被释放)以试图获得垃圾收集来恢复它的内存,那么映射本身仍然会保留它的项目,即仍然引用它的项目。那么你可以用WeakMap来解决这个问题。

WeakMap是Map的变种,它们的大部分外在行为特征都是一样的。区别在于内存分配(垃圾回收)的工作方式。也可以说地图是强参照,WeakMap是弱参照。

WeakMap只接受对象作为键。与Map不同,Map可以使用对象或简单类型作为键。这些对象是弱持有的,这意味着如果对象本身被垃圾收集,WeakMap中的该项也将被删除。

WeakMap的API类似于Map类型的API,但是WeakMap没有clear()和size方法/属性,不会在键、值或项上暴露任何迭代器。

const m = new WeakMap()


常数x = {

id:“007”

}


M.set(x,'玲玲颜料')


m.has(x) // true

复制代码

像地图一样,weakMap可以将信息与对象进行软关联。这个函数在对这个对象没有完全控制权的时候特别有用!比如DOM元素。如果作为映射键的对象可以被删除并且支持垃圾收集,那么WeakMap就是合适的候选对象。

注意:重要的是WeakMap只是弱引用它的键,而不是它的值。

const m = new WeakMap()


const x = {id:1},

y = {id:2}


m.set(x,y)


X = null //垃圾收集

Y = null //无垃圾收集

复制代码

一组

Set是一组值,其中的值是唯一的(重复的将被忽略)。

Set的API类似于Map。只是添加(...)方法取代了get(...),而且没有得到(...)方法。

const s =新集合()


const x = {id:1},

y = {id:2}


s.add(x)

s.add(y)

s.add(x)


s.size. // 2


代尔特(y)

s.size. // 1


透明玻璃()

标准尺寸// 0

复制代码

设置(...)构造函数类似于Map(...),并且可以接受一个iterable,比如另一个集合或者只是一个值数组。不像地图(...)接受条目列表(键/值数组的数组)、集合(...)接受值列表(vlaue),这是一个值数组。

const x = {id:1},

y = {id:2}


const s = nwe集合([x,y])

复制代码

设置(...)不需要得到(...)因为它不会从集合中取值,而是使用has(...)来确定一个值是否存在。

const x = {id:1},

y = {id:2}


const s = nwe集合([x])


s.has(x) // true

s.has(y) // false

复制代码

中的比较算法有(...)和Object.is差不多(...)除了-0和0会被同等对待,没有任何区别。

集合迭代器

Set的迭代器和Map的迭代器一样。对于Set来说,两者的行为特征不同,但它与Map迭代器的行为是成对的。

const s =新集合()


const x = {id:1},

y = {id:2}


S.add(x)。add(y) //没错!支持连锁呼叫


常量键= [...s.keys() ],

值= [...s.values() ],

条目= [...s.entries() ]


keys[0] === x // true

keys[1] === y // true


值[0] === x // true

values[1] === y // true


条目[0][0] === x // true

条目[0][1] === x // true


条目[1][0] === y // true

条目[1][1] === y // true

复制代码

钥匙(...)和价值观(...)迭代器都列出了集合中yieId的不重复值的列表。条目(...)迭代器yieId列出了一个项的数组,其中数组的两项是唯一的集合值。集合的默认迭代器是它的值(...)迭代器。

Set固有的唯一性是它最有用的特性!

const s =新集合([1,2,3,1,' 1 ',6])


const arr = [...s ]


arr // [1,2,3,' 1 ',6]

复制代码

集合的唯一性不允许强制转换,即1和' 1 '是不同的值。

应用场景

标签页:比如在日常开发中,一个项目的标签页表头标签页可以通过Set来维护,因为它是唯一的,不会因为你点击了同一个页面而出现相同的标签页。

阵列重复数据消除

WeakSet

就像WeakMap弱引用它的键一样,WeakSet弱引用它的值(Set没有键)。

const s =新武器集()


const x = {id:1},

y = {id:2}


s.add(x)。添加(y)


X = null // x可以垃圾回收。

Y = null // y可以垃圾回收。

复制代码

注意:WeakSet的值必须是一个对象,而不是像Set那样的原生类型值。

摘要

Map是一个键-值对,其中的值不仅是一个字符串/本机类型,也是一个对象。是集合成员值的唯一列表(任何类型)。

WeakMap也是一个Map,其中的键(对象)是弱引用的,所以当它是对这个对象的最后一个引用时,垃圾回收可以回收这个项。WeakSet也是一个集合,其中的值是弱引用的,这意味着如果其中的项是对该对象的最后一次引用,则可以对其进行垃圾回收。


本文链接:https://www.kkkliao.cn/?id=243 转载需授权!

分享到:

添加博主微信共同交流探讨信息差网赚项目: 19528888767 , 请猛戳这里→点我添加

版权声明:本文由廖万里的博客发布,如需转载请注明出处。

“面试高频的ES6中的Map和Set数据结构详解” 的相关文章

不打游戏只看视频,骁龙和天玑竟然能拉开这么大差距?

不打游戏只看视频,骁龙和天玑竟然能拉开这么大差距?

事情是这样的。最近托尼有位同事因为之前被使用三星 4nm 工艺的骁龙 8 Gen 1 折腾怕了,所以他在把原来的旧手机卖了之后,转手换了台搭载天玑 9000 的手机。一开始他对这台手机可以说非常满意,打游戏时发热终于没那么严重了,然而时间一长,他发现手机电量貌似掉的有点快,续航并没有想象中那么顶。本...

外媒惊呼,继智能手机之后,中国在这一领域强势崛起!

外媒惊呼,继智能手机之后,中国在这一领域强势崛起!

作为一个80后,我清楚地记得,在功能手机的时代,中国的手机厂商一个能打的都没有,很多人为此努力过,但最后都折戟沉沙,黯淡收场。彼时的中国手机市场完全是三星、诺基亚、摩托罗拉等外资品牌的市场,仅存的中国手机厂商只能在夹缝中生存,靠生产杂牌手机苟延残喘。那时候我们多么希望能崛起一个自主品牌、民族品牌来收...

开始“反扑”了?中国院士正式宣布,厉害的不只有华为

开始“反扑”了?中国院士正式宣布,厉害的不只有华为

随着中国的这些年的不断发展,中国在各方都取得了很大的成就,现在的中国已经发生了翻天覆地的变化。在生活上。我们进入了电商时代,进入了移动支付时代;在科技上,我们进入了智能互联网时代,即将迎来的是全新的5G时代,还有很多很多中国制造都是值得我们骄傲的。特别值得提及的就是,现在中国制造已经成为了我们的一个...

美媒:ASML开始被中企无情地“打脸”了

美媒:ASML开始被中企无情地“打脸”了

点击关注,每天精彩不断!导读:美媒:ASML开始被中企无情的“打脸”了!自从我国华为公司被打压以后,很多科技企业都意识到了自主研发和生产半导体芯片的重要性;而想要生产芯片并不是一件容易的事情;作为21世纪最伟大的发明之一,半导体芯片如今在整个科技领域都起着至关重要的作用,虽然说一个小小的芯片看上去只...

骁龙8和骁龙8+的日常体验,到底有多大差距?

骁龙8和骁龙8+的日常体验,到底有多大差距?

机哥写过一篇文章。主要呢,就是盘点了今年那些跳水比较严重的旗舰手机。像什么OPPO Find X5 Pro天玑版啊、小米12 Pro啊、一加10 Pro啊等等。相比起刚上市时定价,现在这些机型,普遍的降价幅度都超过了2000块。原本是卖5000多,现在却只卖3000多。。机哥当时发完文章,底下很大一...

步步高创始人段永平,高手有所为有所不为,35条深度思考值得收藏

步步高创始人段永平,高手有所为有所不为,35条深度思考值得收藏

段永平,一个注定在商业史无法被忽视的存在。段永平的经历可谓传奇。他是国内第一个拍下来股神巴菲特午餐的男人,那时候他还带上了现在拼多多的创始人黄铮。而这个一手创办了小霸王、步步高等著名企业,并与Vivo、OPPO、一加和拼多多有着千丝万缕联系的企业家,这位通过投资网易、腾讯和苹果而获利颇丰的投资者,也...