c++ map的存储结构_ES6专题—数据结构(8)

ee72a5329530103a1f420da22f466a5f.png

小伙伴们,你是不是曾经为数组去重感到烦恼,也为对象的key值只能储存字符串而感到无奈,是不是觉得Array和Object不够强大,不能满足项目开发中日益增长的需求?

今天,我们学习ES6中新出现的Set、WeakSet、Map、WeakMap,他们是对 JavaScript 关于集合概念的补充,将把你的困惑烦恼一点点解开。

1、Set

1.1 Set简介

Set 类似于数组,是一种集合的数据结构, 根据中学数学的知识,我们知道集合中的所有元素都是唯一的,所以

Set 和 Array 之间最大的区别是:

Set中所有的成员都是唯一的。当然,也可以把Set想象成是一个既没有重复元素,也没有顺序概念的数组。

ee5bc391682130561a589b575ab05302.png

Set 本身是一个构造函数,用来生成 Set 数据结构

const s1 = new Set()
s1.add(1)
s1.add(2)
s1.add(1)
// s1 的值为 {1, 2}

Set 函数可以接受一个可循环的数据结构(如数组、类数组、含有 iterable 接口 的其他数据结构)作为参数来初始化:

const s2 = new Set([1,2,1,4,3,4])
// s2 的值为 {1, 2, 3, 4}

1.2 Set属性及操作方法

(1) size

const s2 = new Set(['a','b','c','d']);
// s2 的值为 {"a", "b", "c", "d"}
s2.size // 4

(2) add(value)

向 Set 中添加一个值,返回 Set 结构本身,所以可以使用链式调用

s2.add('e').add('f').add('g');
// s2 的值为 {"a", "b", "c", "d", "e", "f", "g"}

(3) delete(value)

删除一个值,返回布尔值表示是否成功删除

s2.delete('c')
// 返回true,表示成功删除
// s2 的值为 {"a", "b", "d", "e", "f", "g"}

(4) has(value)

Set 中是否包含某个值,返回布尔值

s2.has('d')
// 返回true,表示存在"d"
s2.has('y')
// 返回false,表示不存在"y"

(5) clear()

清除所有成员,无返回值

s2.clear()
// Set(0) {}

1.3 Set循环

Set结构的实例可以用如下方式循环成员:

keys():返回键名values():返回键值entries():返回所有成员forEach():循环Set实例所有成员。

let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
 console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
 console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
 console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
​
//可以省略values方法,直接用for...of循环遍历Set。
for (let x of set) {
 console.log(x);
}
// red
// green
// blue

由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以key方法和value方法的行为完全一致。

entries方法返回的同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。

使用forEach方法进行循环:

let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(value + 2) )
// 3
// 4
// 5

1.4 Set应用

(1) 数组去重

以前,

function uniq(array){
    var temp = []; //一个新的临时数组
    for(var i = 0; i < array.length; i++){
        if(temp.indexOf(array[i]) == -1){
            temp.push(array[i]);
        }
    }
    return temp;
}
​
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));

现在:

let array = [1,2,1,4,5,3,'1'];
[...new Set(array)]     // [1, 2, 4, 5, 3, "1"]

怎么样?是不是简单的让人发指?

(2) 实现并集、交集、差集

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));// Set {1}

2、WeakSet

2.1 WeakSet简介

"weak" 有微弱、薄弱的意思,这意味着WeakSet 是 Set 的阉割版,功能要弱一些。

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。

const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set

其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

这一特性使WeakSet性能要比map要高,对于存储无顺序要求的,不重复的,临时存储的场景,可以使用它。

837fa28b306e08b0e957555db181ba75.png

2.2 WeakSet的特点

(1)WeakSet没有size属性

(2)不能循环,因为内部数据随时可能消失,循环机制无法保证数据存在

(3)使用的WeakSet的优势是使用完后被垃圾回收,减少内存资源浪费。

3、Map

3.1 Map简介

JavaScript的对象 Object 本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适、更强大。

2e168b8ec767610d6bea9cc45807e2b2.png
const m1 = new Map()
const k = {a:3}
​
m1.set(k, 'mapValue')  // { {a:3} => "mapValue"}, 对象也能档 key,真太便利啦
m1.get(k)   // mapValue

Map 还可以接受一个数组作为参数,这个数组的成员是一个个表示键值对的数组

const m2 = new Map([
    ['name', '大彬哥'],
    ['age', 18]
])
// {'name'=>'大彬哥', 'age'=>18}

注意:只有对同一个对象的引用,Map结构才将其视为同一个键

var map = new Map();
map.set(['a'], 111);
map.get(['a']) // undefined   

上面代码的set和get方法,表面是针对同一个键,但实际上 ['a'] 是两个值,两个 ['a'] 内存地址是不一样的,因此get方法无法读取该键,返回undefined。

因此,同样的值的两个实例,在Map结构中被视为两个键:

var map = new Map();
var obj1 = ['a'];
var obj2 = ['a'];
map.set(obj1, 111);
map.set(obj2, 222);
map.get( obj1 );  //111
map.get( obj2 );  //222

这样设计的好处是:

Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。

如果Map的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map将其视为一个键,包括0和-0。另外,虽然NaN不严格相等于自身,但Map将其视为同一个键。

let map = new Map();
​
map.set(NaN, 666);
map.get(NaN) // 666
​
map.set(-0, 888);
map.get(+0) // 888

3.2 Map属性及操作方法

(1) size

const m2 = new Map([
    ['name', '大彬哥'],
    ['age', 18]
])
m2.size // 2

(2) set(key, value)、get(key)

设置一个键名为 key 键值为 value 的成员,并返回整个 Map 结构,如果已经存在该键名,则会更新键值

m2.set('sex', 'boy')
// {"name" => "大彬哥", "age" => 18, "sex" => "boy"}
m2.set('name', '涂涂')
// {"name" => "涂涂", "age" => 18, "sex" => "boy"}

读取这个 key 对应的值,如果没有则返回 undefined

m2.get('sex')
// "boy"

(3) has(key) 、 delete(key) 、clear()

m2.has('age')  // true
m2.delete('age')  // true , 
// m2 变成了 {"name" => "涂涂", "sex" => "boy"}
m2.clear()
// m2 变成了 Map(0) {}

Map 的 has、delete、clear 方法 和 Set 的 has、delete、clear 使用方式十分类似,都是十分语义化的操作命令。

3.3 Map循环

Map实例的循环方式和Set类似,可以用 keys、values、entries、forEach 等方式循环成员

const m2 = new Map([
    ['name', '大彬哥'],
    ['age', 18]
])
for (let key of m2.keys()) {console.log(key)}
// name
// age
​
for (let value of m2. values()) {console.log(value)}
// 大彬哥
// 18
​
for (let item of m2. entries()) {console.log(item[0], item[1])}
// name 大彬哥
// age 18
// 这里的 item 是一个数组,其中第一个值为键名,第二个值为键值
​
m2.forEach((value, key) => console.log(key,value))
// name 大彬哥
// age 18

Map实例的循环方式与Set的不同点在于:Map实例的键名和键值是不同的。

3.4 Map应用

(1) Map 转为 Array,使用扩展运算符 ...

let m_1 = new Map([[1, 'name'], ['name', 'js']]);
[...m_1];  // 返回值为 [[1,"name"],["name","js"]]

(2)Array 转为 Map

数组需要是一个二维数组才可以,并且如果该数组中有元素的key相同的情况,后面的元素的value会覆盖前面的

let arr = [[1, 12], [1, 24], ['name', 'ji'], ['name', 'js']];
let arr2m = new Map(arr);
// 返回值为 Map(2) {1 => 24, "name" => "js"}

4、WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。

不同之处在于,WeakMap 不会阻止它的键值被垃圾回收。那意味着你可以把数据和对象关联起来不用担心内存泄漏。

WeakMap 应用的典型场合就是 DOM 节点作为键名:

var vm = new WeakMap();
var element = document.querySelector('.element');
​
vm.set(element, "Original");
vm.get(element);    // Original
​
element.parentNode.removeChild(element);    // 删除节点,系统会回收element这个Dom节点对象
element = null;
vm.get(element);    // undefined, WeakMap自动删除了element这个成员

5、总结

5.1 Set 与 Map

在使用的过程中明显的Set和Map比我们之前经常使用的Array和Object是有明显的便捷优势的。在后续的开发过程中,我们可以根据场景需要来通过Set和Map来替代Array和Object来使用。

如果对数据结构存储的唯一性有要求,考虑使用Set。

如果数据的复杂程度高,考虑使用Map,在一些构建工具中是非常喜欢使用Map这种数据结构来进行配置,因为map是一种灵活、高效性、适合一对一查找的数据结构。

Set和Map有着类似的API,主要的不同是Set没有set方法,因为它不能存储键值对,剩下的几乎相同。

5.2 WeakMap 与 WeakSet

WeakMap 与 WeakSet 作为一个比较新颖的概念,其主要特点在于弱引用。

相比于 Map 与 Set 的强引用,弱引用可以令对象在 “适当” 情况下正确被垃圾回收,减少内存资源浪费。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值