1.Set数据结构

Set本质上是一个没有重复数据,但是具有irerator接口可以遍历的一种集合。

Set本身也是一种数据结构的构造函数。

1.Set的初始化

var obj = new Set(参数);

上面生成一个Set的实例,obj是集合对象,可以通过for...of遍历。

参数可以是数组,也可以是类数组(具有iterator接口的数据,如字符串)

var obj = [...new Set([1,3,3,3])]; // [1,3]
var obj = [...new Set('hellohello')]; // ['h','e','l','o'].join('')--'helo'

注意new Set()生成的对象是类数组,通过[...]转为数组。

⚠️:[...new Set(数组或者类数组)] 可以去重!!!

// +0/-0是一个值 NaN等于自身
[...new Set([+0,-0,NaN, NaN])]; // [0, NaN]

两个对象总是不相等,该去重方法不适用于对象(任何形式的对象)!

[...new Set([{}, {}, {}])]; // [{}, {}, {}]
// 对象指的的是存储地址,所以每个对象都不一样,所以,不会被认为是重复数据

对象去重参考

⚠️将Set结构转为数组还有一个方法Array.from(set对象);Array.from可以将所有的类数组(含length)转为数组

let obj = Array.from(new Set([1,23,3,3]))

扩展运算符(...)和Array.from() 方法本质也是调用for...of循环。

2.Set的实例属性和方法

1.实例属性

1. Set.prototype.constructor

实例对象的构造函数,继承自原型对象

const obj = new Set('a');
obj.constructor; //Set Set.prototype.constructor === Set

2. Set.prototype.size

实例对象的元素去重后的个数;相当于数组的长度属性

const obj = new Set([1,2,1,2,3]);
obj.size; //
const obj = new Set('aabbccdd');
obj.size; //

2. 操作方法

1. Set.prototype.add(value)

向Set集合添加元素

参数:只能传入一个参数;参数可以是任意类型

返回:添加元素后的Set实例

const set = new Set();
const result = set.add(1);
result.size; // 1 说明result是Set结构,且是添加元素之后的Set结构

由返回值可知:add方法可以连写

const set = new Set();
const result = set.add(1).add('a').add(true);
[...result]; //[1,'a',true]

2. Set.prototype.delete(value)

删除Set集合内的值

参数:待删除的元素

返回: 布尔值;表示是否删除成功

const set = new Set();
set.add(1).add(2).add(3);
const result = set.delete(2);
result; // true 删除成功
set.size; //
const result2 = set.delete(5);
result2; // false Set集合不包括5,删除失败

3. Set.prototype.has(value)

判断Set数据集合中是否含有某元素

参数: 待判断数据

返回: 布尔值;表示是否包含该数据

const set = new Set();
set.add(1).add(2).add(3);
set.has(1); // true
set.has(5); // false

4. Set.prototype.clear()

清空Set集合

参数: 无

返回:无(undefined)

const set = new Set();
set.add(1).add(2).add(3);
set.size; //
set.clear();
set.size; //

3. 遍历方法

Set集合的遍历顺序是元素的插入顺序。和元素的数据类型无关。

不同于对象的属性遍历顺序,根据属性遍历的先后顺序和属性的数据类型有关。

1. Set.prototype.keys()

同2

2.Set.prototype.values()

由于Set集合中只有value,没有key,所以默认keys和values都返回成员的值。

返回:一个遍历器;遍历器成员是集合的值; set.keys()/set.values()返回值一摸一样

values()方法是Set默认的[Symbol.iterator]属性对应的函数。

const set = new Set([1,2,4]);
const keys = set.keys(); // SetIterator
for (let key of keys()) {
console.log(key)
}
// 运行结果如下:
1
2
4 const values = set.values(); //SetIterator
[...values]; //[1,2,4] ...扩展运算符本质上调用的是for...of

3. Set.prototype.entries()

返回: 一个遍历器;遍历器每个成员是一个数组,数组含有相同的两个值。

let set = new Set();
set.add({a:1}).add(1).add(true);
const entries = set.entries();
for(let entity of entries) {
console.log(entity);
}
// 运行结果如下:
[{a:1}, {a:1}]
[1,1]
[true, true]

4. Set.prototype.forEach(fn)

用法和数组的 forEach基本一致

const set = new Set([1,2,3]);
set.forEach(function(value, key){ //value和key永远相等;数组中的key是index
console.log(value,key);
}, thisObj)
// 运行结果如下:
1,1
2,2
3,3

3. Set应用

1. 去重

// 数组去重
[...new Set([1,2,3,1,3,5])]; //[1,2,3,5]
Array.from(new Set([1,2,3,1,3,5])); // [1,2,3,5] // Set的每个成员*2;先变为数组
new Set([...new Set([1,2,3,1,3,5])].map(i => i*2)); // Set{2,4,6,10}
new Set(Array.from(new Set([1,2,3,1,3,5]), i => i*2)); //Set{2,4,6,10}

2.求集合

假如现有两个集合:let setA = new Set([1,2,3]);  let setB = new Set([1,3,5]);

1. 并集

const union = new Set([...setA, ...setB])
// Set{1,2,3,5}

2. 交集

const intersect = new Set([...setA].filter(i => setB.has(i)));
// Set{1,3}

3. 差集

const difference = new Set([...setA].filter(i => !setB.has(i)));
// Set{2} setA有;setB没有

二. WeakSet数据结构

1. 初始化

大体和Set相同;也是一个构造方法。

const ws = new WeakSet();

可以通过数组或者类数组传参,元素类型必须是对象

const ws = new WeakSet([{a:1}, {b:2}]);
// 即数组内的每个元素必须是对象类型

2. 和Set不同点:

1. 元素只能是对象

WeakSet数据集合中元素只能是对象(普通对象,函数,数组等);不能是原始类型的值。

因此,add(value)的参数必须是对象,delete/has的参数数据类型可以是任意类型

const ws = new WeakSet();
// add(value)参数只能是对象
ws.add({a:1}); // 普通对象
ws.add([1,2]); // 数组
// delele/ has不要求参数数据类型

2. 不能遍历。

1. 实例对象不含[Symbol.iterator]属性,不能遍历;

所以,不能通过...或者Array.from将实例WeakSet数据转为数组

2. 不含size属性;

3. 没有forEach(), keys(), values(), entries()遍历方法。

原因:

该数据结构中的对象引用是弱引用,垃圾回收机制不考虑WeakSet对对象的引用。

即某对象在WeakSet之外没有其他的引用,则该对象即使在WeakSet中有引用,也会被垃圾回收处理掉。

因为元素对象的个数会根据外部引用情况而变化,所以不允许其遍历。

3. 不能清空

没有clear方法

3. 应用

对于引用的一些已经删除的DOM节点,已经不再需要,对于强引用的Set结构,

这些垃圾数据不会被垃圾回收机制自动清理掉,仍占用内存, 需要手动delete删除来释放内存;

但是WeakSet允许垃圾回收机制清理掉这些垃圾数据,释放内存。

三. Map数据结构

1. Map基础

1. 含义

Map是一种键值对的数据结构;不同于对象的键值只能是字符串和Symbol,

Map结构的key可以是任意类型的数据。

是一种更广义的值-值的映射。

2. 初始化

1. 初始化空值

const map = new Map();
const keyObj = {a:1}; map.set(keyObj, 'content');

2. 带参数初始化;可以传入元素是长度为2的数组或者具有iterator接口的数据类型

const map = new Map([[1,2],[{a:2}, 5]]);
// 等同于
const set = new Set([[1,2],[{a:2}, 5]]);
const map = new Map(set)

2. Map实例属性和方法

1. 实例属性

1.Map.prototype.size

元素的个数

const map = new Map();
map.set('foo', true);
map.set('bar', false); map.size //

2. 操作方法

1. Map.prototype.set(key, value)

向Map结构添加值

返回map实例,可以链式写法;

当添加相同的键名时,后面的键名对应的值会覆盖前面的。

const map = new Map();
map.set('foo', true).set('foo', 2);
map.size; //
map.get('foo'); //

当key值是对象类型时,注意除非该对象赋值给一个遍历,使用时通过变量时会认为是同一个值;

直接写对象(任何对象类型),都相当于新建了一个对象,它代表的是内存地址。

// 赋值给遍历
const map = new Map();
const arrObj = ['a'] map.set(arrObj, 555);
map.get(arrObj) // // 直接使用
map.set(['b'], 666);
map.get(['b']) //undefined set,get中的['b']是不同的内存地址地址对应的值
// 即使值一样也对应不同的地址,对应Map的key来说是不同的值
const k1 = ['c'];
const k2 = ['c']; //k1,k2值相同,但是内存地址不同
map.set(k1, 5);
map.get(k2) //undefined

2. Map.prototype.get(key)

获取键名对应的值

3. Map.prototype.has(key)

判断某个键名是否存在

4. Map.prototype.delete(key)

删除某个键名对应的键值对

5.Map.prototype.clear()

清空整个Map数据结构

3. 遍历方法

1. Map.prototype.keys()

返回键名的遍历器

2. Map.prototype.values()

返回值的遍历器

3.Map.prototype.entries()

返回键值对的遍历器

Map结构的[Symbol.iterator]属性对应的是entries()方法

const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]); [...map.keys()]
// [1, 2, 3] [...map.values()]
// ['one', 'two', 'three'] [...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']] [...map] //Map函数生成的实例本身也可遍历
// [[1,'one'], [2, 'two'], [3, 'three']]

4.Map.prototype.forEach(fn)

同Set

4. 和其他数据结构互换

1. Map->数组

const map = new Map([[1,2],[{a:2}, 5]]);
[...map]; // [[1,2],[{a:2}, 5]]

2. 数组->Map

数组必须满足每个成员都是长度为2的数组

new Map([
[true, 7],
[{foo: 3}, ['abc']]
])

3. Map->对象


Object.fromEntries(new Map([
   [true, 7],
   [{foo: 3}, ['abc']]
])) //{true: 7, [object Object]: ['abc']}

// 键值是对象时,会自动将对象转为对应的字符串

4. 对象->Map

new Map([...Object.entries({yes: true, no: false})])

5. Map->JSON

1)键名都是字符串

先转为对象,再转JSON

const set = new Map().set('yes', true).set('no', false);
function toJSON() {
return JSON.stringify(Object.fromEntries(set))
}

2)键名有非字符串

转数组,再转JSON

const set = new Map().set('yes', true).set('no', false);
function toJSON() {
return JSON.stringify([...set]))
}

6. JSON->Map

5的逆运算

都需要先JSON.parse()后再根据情况转换。

四.WeakMap

1. 初始化

const wp = new WeakMap();
wp.set({a:1}, 1) //键名必须是对象
//
const wp = new WeakMap([[{a:1}, 1],[{a:2}, 2]]) //键名必须是对象

2.和Map差别

1. 键名只能是对象

2. 不能遍历。

原因:第4项

1. 没有size属性

2.没有forEach,keys,values,entries的遍历方法

3. 只有set,get,delete,has四个方法

3.无法清空

没有clear方法

4.键名对应的对象不计入垃圾回收机制。

3.应用-防止内存泄漏。

键名对应的对象是弱引用,不计入垃圾回收机制。只是键名!!

对于值是对象的情况,即使外部删除了引用,WeakMap内存仍然存在

const wm = new WeakMap();
let key = {};
let obj = {foo: 1}; wm.set(key, obj);
obj = null; // 将对象设置为null, 手动删除引用
wm.get(key);// {foo: 1}内部仍存在

首先,对象用在普通数据结构中,是强引用,不手动删除的话,会一直占用内存。

const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
[e1, 'foo 元素'],
[e2, 'bar 元素'],
];
// e1, e2是两个NodeList对象,用于二维数组中,相当于被二维数组引用,就会占用内存存放;
// arr之外其实e1,e2对应的DOM已经不存在,也不会自动被垃圾回收机制回收掉,只能手动删除,清空数组,arr.length = 0;

而WeakMap结构是弱引用,如果出现上面例子中的情况,会自动被垃圾回收机制回收掉,释放内存。

验证:

使用process.memoryUsage()查看内存;

// 首先打开node命令行
node --expose-gc // 允许手动执行垃圾回收机制 // 手动执行一次垃圾回收,保证获取的内存使用状态准确
> global.gc();
undefined // 查看内存占用的初始状态,heapUsed 为 4M 左右
> process.memoryUsage();
{ rss: 21106688,
heapTotal: 7376896,
heapUsed: 4153936,
external: 9059 } > let wm = new WeakMap();
undefined // 新建一个变量 key,指向一个 5*1024*1024 的数组
> let key = new Array(5 * 1024 * 1024);
undefined // 设置 WeakMap 实例的键名,也指向 key 数组
// 这时,key 数组实际被引用了两次,
// 变量 key 引用一次,WeakMap 的键名引用了第二次
// 但是,WeakMap 是弱引用,对于引擎来说,引用计数还是1
> wm.set(key, 1);
WeakMap {} > global.gc();
undefined // 这时内存占用 heapUsed 增加到 45M 了
> process.memoryUsage();
{ rss: 67538944,
heapTotal: 7376896,
heapUsed: 45782816,
external: 8945 } // 清除变量 key 对数组的引用,
// 但没有手动清除 WeakMap 实例的键名对数组的引用
> key = null;
null // 再次执行垃圾回收
> global.gc();
undefined // 内存占用 heapUsed 变回 4M 左右,
// 可以看到 WeakMap 的键名引用没有阻止 gc 对内存的回收
> process.memoryUsage();
{ rss: 20639744,
heapTotal: 8425472,
heapUsed: 3979792,
external: 8956 }

垃圾回收机制

示例1: DOM节点相关

let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap(); myWeakmap.set(myElement, {timesClicked: 0}); myElement.addEventListener('click', function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
}, false);
//一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。

示例二:部署类的私有属性

// Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏
const _counter = new WeakMap();
const _action = new WeakMap(); class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
} const c = new Countdown(2, () => console.log('DONE')); c.dec()
c.dec()
// DONE

详情参考

数据结构---Set和Map的更多相关文章

  1. paip.提升性能---并行多核编程哈的数据结构list,set,map

    paip.提升性能---并行多核编程哈的数据结构list,set,map vector/copyonwritearraylist 都是线程安全的. 或者经过包装的list ::: collection ...

  2. ES6——新增数据结构Set与Map的用法

    ES6 提供了新的数据结构 Set以及Map,下面我们来一一讲解. 一.Set 特性 似于数组,但它的一大特性就是所有元素都是唯一的,没有重复. 我们可以利用这一唯一特性进行数组的去重工作. 1.单一 ...

  3. 数据结构逆向分析-Map

    数据结构逆向分析-Map map是一个典型的二叉树结构,准确的来说是一个平衡二叉树或者红黑树,特点是数据存储是有序的存储. 参考侯杰老师的stl源码剖析,map里面采用的是RB-TREE也就是红黑树 ...

  4. es6学习笔记--新数据结构Set,Map以及WeakSet,WeakMap

    在javascript中,存储数据的方式大部分就是以数组或者对象形式存储的,es6出现了4种新集合Set,Map,WeakSet,WeakMap来存储数据,简化了编程. 集合--Set 类似于数组,但 ...

  5. Matlab 中实用数据结构之 containers.Map

    概要   熟悉 Python 的都知道字典 Dict 类型数据结构功能的强大,Matlab 中虽然有表结构,但是其列名必须是亦变量名类型的字符串,如果我想用数字开头的字符串作键值,其表结构就无能为力了 ...

  6. Java核心数据结构(List、Map、Set)原理与使用技巧

    JDK提供了一组主要的数据结构实现,如List.Set等常用数据结构.这些数据都继承自java.util.Collection接口,并位于java.util包内. 一.List接口 最重要的三种Lis ...

  7. java数据结构5--集合Map

    Map Map与Collection在集合框架中属并列存在 Map存储的是键值对<K,V> Map存储元素使用put方法,Collection使用add方法 Map集合没有直接取出元素的方 ...

  8. ES6数据结构Set、Map

    一.Set数据结构 Set是无序的不可重复的多个value的集合体,Set结构是类似于数组结构,但是Set中的值都不能重复 常用的属性和方法 size:返回set实例的成员总数 add():添加某个值 ...

  9. 11 Set和Map数据结构

    Set和Map数据结构 Set WeakSet Map WeakMap 首先 这四个对象都是 数据结构. 用于存放数据 Set 类似数组. 但是不能重复. 如果你有重复数据,会自动忽略 属性 size ...

随机推荐

  1. 大话OSI七层协议

    大白话OSI七层协议 互联网的本质就是一系列的网络协议,这个协议就叫OSI协议(一系列协议),按照功能不同,分工不同,人为的分层七层.实际上这个七层是不存在的.没有这七层的概念,只是人为的划分而已.区 ...

  2. spring cloud微服务实践七

    在spring cloud 2.x以后,由于zuul一直停滞在1.x版本,所以spring官方就自己开发了一个项目 Spring Cloud Gateway.作为spring cloud微服务的网关组 ...

  3. Yii2.0 RESTful API 之速率限制

    Yii2.0 RESTFul API 之速率限制 什么是速率限制? 权威指南翻译过来为限流,为防止滥用,你应该考虑对您的 API 限流. 例如,您可以限制每个用户 10 分钟内最多调用 API 100 ...

  4. 附录:ARM 手册 词汇表

    来自:<DDI0406C_C_arm_architecture_reference_manual.pdf>p2723 能够查询到:“RAZ RAO WI 等的意思” RAZ:Read-As ...

  5. Java RadixSort

    Java RadixSort /** * <html> * <body> * <P> Copyright 1994-2018 JasonInternational ...

  6. tiny-Spring【2】逐步step分析-新加入特性

    tiny-Spring是黄亿华大佬自己写的一个集合IOC和AOP于一身的一种轻量级[教学用]Spring框架,它的github库地址为:https://github.com/code4craft/ti ...

  7. 监控SQL:通过SQL Server的DDL触发器来监控数据库结构的变化(1)

    原文:监控SQL:通过SQL Server的DDL触发器来监控数据库结构的变化(1) 如果你要同步不同数据库之间的数据,首先会想到的是数据库复制技术,但如果让你同步数据库的结构,你会想到什么呢? 下面 ...

  8. Down State Flush Feature

    Down State Flush Feature ========================================================== Citrix NetScaler ...

  9. 客户端相关知识学习(十一)之Android H5交互Webview实现localStorage数据存储

    前言 最近有一个需求是和在app中前端本地存储相关的,所以恶补了一下相关知识 webView开启支持H5 LocalStorage存储 有些时候我们发现写的本地存储没有起作用,那是因为默认WebVie ...

  10. class类 - extends

    继承是面向对象中一个比较核心的概念.ES6 class的继承与java的继承大同小异,如果学过java的小伙伴应该很容易理解,都是通过extends关键字继承.相较于ES5当中通过原型链继承要清晰和方 ...