1.Set数据结构

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

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

1.Set的初始化

  1. var obj = new Set(参数);

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

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

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

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

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

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

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

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

对象去重参考

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

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

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

2.Set的实例属性和方法

1.实例属性

1. Set.prototype.constructor

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

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

2. Set.prototype.size

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

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

2. 操作方法

1. Set.prototype.add(value)

向Set集合添加元素

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

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

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

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

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

2. Set.prototype.delete(value)

删除Set集合内的值

参数:待删除的元素

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

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

3. Set.prototype.has(value)

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

参数: 待判断数据

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

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

4. Set.prototype.clear()

清空Set集合

参数: 无

返回:无(undefined)

  1. const set = new Set();
  2. set.add(1).add(2).add(3);
  3. set.size; //
  4. set.clear();
  5. 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]属性对应的函数。

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

3. Set.prototype.entries()

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

  1. let set = new Set();
  2. set.add({a:1}).add(1).add(true);
  3. const entries = set.entries();
  4. for(let entity of entries) {
  5. console.log(entity);
  6. }
  7. // 运行结果如下:
  8. [{a:1}, {a:1}]
  9. [1,1]
  10. [true, true]

4. Set.prototype.forEach(fn)

用法和数组的 forEach基本一致

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

3. Set应用

1. 去重

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

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

2. 交集

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

3. 差集

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

二. WeakSet数据结构

1. 初始化

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

  1. const ws = new WeakSet();

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

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

2. 和Set不同点:

1. 元素只能是对象

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

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

  1. const ws = new WeakSet();
  2. // add(value)参数只能是对象
  3. ws.add({a:1}); // 普通对象
  4. ws.add([1,2]); // 数组
  5. // 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. 初始化空值

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

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

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

2. Map实例属性和方法

1. 实例属性

1.Map.prototype.size

元素的个数

  1. const map = new Map();
  2. map.set('foo', true);
  3. map.set('bar', false);
  4.  
  5. map.size //

2. 操作方法

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

向Map结构添加值

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

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

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

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

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

  1. // 赋值给遍历
  2. const map = new Map();
  3. const arrObj = ['a']
  4.  
  5. map.set(arrObj, 555);
  6. map.get(arrObj) //
  7.  
  8. // 直接使用
  9. map.set(['b'], 666);
  10. map.get(['b']) //undefined set,get中的['b']是不同的内存地址地址对应的值
  11. // 即使值一样也对应不同的地址,对应Map的key来说是不同的值
  12. const k1 = ['c'];
  13. const k2 = ['c']; //k1,k2值相同,但是内存地址不同
  14. map.set(k1, 5);
  15. 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()方法

  1. const map = new Map([
  2. [1, 'one'],
  3. [2, 'two'],
  4. [3, 'three'],
  5. ]);
  6.  
  7. [...map.keys()]
  8. // [1, 2, 3]
  9.  
  10. [...map.values()]
  11. // ['one', 'two', 'three']
  12.  
  13. [...map.entries()]
  14. // [[1,'one'], [2, 'two'], [3, 'three']]
  15.  
  16. [...map] //Map函数生成的实例本身也可遍历
  17. // [[1,'one'], [2, 'two'], [3, 'three']]

4.Map.prototype.forEach(fn)

同Set

4. 和其他数据结构互换

1. Map->数组

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

2. 数组->Map

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

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

3. Map->对象

  1.  

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

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

4. 对象->Map

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

5. Map->JSON

1)键名都是字符串

先转为对象,再转JSON

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

2)键名有非字符串

转数组,再转JSON

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

6. JSON->Map

5的逆运算

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

四.WeakMap

1. 初始化

  1. const wp = new WeakMap();
  2. wp.set({a:1}, 1) //键名必须是对象
  3. //
  4. 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内存仍然存在

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

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

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

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

验证:

  1. 使用process.memoryUsage()查看内存;
  2.  
  3. // 首先打开node命令行
  4. node --expose-gc // 允许手动执行垃圾回收机制
  5.  
  6. // 手动执行一次垃圾回收,保证获取的内存使用状态准确
  7. > global.gc();
  8. undefined
  9.  
  10. // 查看内存占用的初始状态,heapUsed 为 4M 左右
  11. > process.memoryUsage();
  12. { rss: 21106688,
  13. heapTotal: 7376896,
  14. heapUsed: 4153936,
  15. external: 9059 }
  16.  
  17. > let wm = new WeakMap();
  18. undefined
  19.  
  20. // 新建一个变量 key,指向一个 5*1024*1024 的数组
  21. > let key = new Array(5 * 1024 * 1024);
  22. undefined
  23.  
  24. // 设置 WeakMap 实例的键名,也指向 key 数组
  25. // 这时,key 数组实际被引用了两次,
  26. // 变量 key 引用一次,WeakMap 的键名引用了第二次
  27. // 但是,WeakMap 是弱引用,对于引擎来说,引用计数还是1
  28. > wm.set(key, 1);
  29. WeakMap {}
  30.  
  31. > global.gc();
  32. undefined
  33.  
  34. // 这时内存占用 heapUsed 增加到 45M 了
  35. > process.memoryUsage();
  36. { rss: 67538944,
  37. heapTotal: 7376896,
  38. heapUsed: 45782816,
  39. external: 8945 }
  40.  
  41. // 清除变量 key 对数组的引用,
  42. // 但没有手动清除 WeakMap 实例的键名对数组的引用
  43. > key = null;
  44. null
  45.  
  46. // 再次执行垃圾回收
  47. > global.gc();
  48. undefined
  49.  
  50. // 内存占用 heapUsed 变回 4M 左右,
  51. // 可以看到 WeakMap 的键名引用没有阻止 gc 对内存的回收
  52. > process.memoryUsage();
  53. { rss: 20639744,
  54. heapTotal: 8425472,
  55. heapUsed: 3979792,
  56. external: 8956 }

垃圾回收机制

示例1: DOM节点相关

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

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

  1. // Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏
  2. const _counter = new WeakMap();
  3. const _action = new WeakMap();
  4.  
  5. class Countdown {
  6. constructor(counter, action) {
  7. _counter.set(this, counter);
  8. _action.set(this, action);
  9. }
  10. dec() {
  11. let counter = _counter.get(this);
  12. if (counter < 1) return;
  13. counter--;
  14. _counter.set(this, counter);
  15. if (counter === 0) {
  16. _action.get(this)();
  17. }
  18. }
  19. }
  20.  
  21. const c = new Countdown(2, () => console.log('DONE'));
  22.  
  23. c.dec()
  24. c.dec()
  25. // 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. Thinking In Java 4th Chap6 访问权限控制

    引入一个包及其所包含的方法:import java.util.ArrayList;(引入java.util包,并引入了包中的ArrayList类) import java.util.*;(引入了jav ...

  2. python之文件读写操作笔记

    对不同类的文件操作,需要调用相关的库文件,一般情况下,可以选择建立:写文件函数和读文件函数.在写文件与读文件函数中 我们可以采用:with  open('文件名','w', encoding='utf ...

  3. SAS学习笔记16 SAS创建计数(枚举)变量

  4. Asp.net core 学习笔记 2.2 migration to 3.0

    Ef core 3.0 一些要注意的改变 refer : https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaki ...

  5. Centos6 yum安装nginx

    1.Centos6系统库中默认是没有nginx的rpn包的,所以我们需要先更新下rpm依赖库 (1):使用yum安装nginx,安装nginx库 rpm -Uvh http://nginx.org/p ...

  6. HTML 禁止复制文字

    因为本人平时喜欢看网络小说,但是喜欢看的文通过正经网站或者app都需要收费,让人很是不爽,所以...总之,百度网盘上资源很多.但是问题来了,这些资源肯定不会是作者自己流出的,也不应该是网站或app流出 ...

  7. LeetCode 1103. Distribute Candies to People

    1103. Distribute Candies to People(分糖果||) 链接:https://leetcode-cn.com/problems/distribute-candies-to- ...

  8. js判断是哪种浏览器和阻止页面加载

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  9. JavaScript Drum kit

    用 JavaScript 实现网页鼓乐器,相关的初始代码在 JavaScript30 官网和 GitHub 上已经存在.我把 sound 文件夹下的音频全部替换掉了,一些相关解释也直接在注释中标明. ...

  10. Linux Centos7配置ftp服务器

    一.安装 1.安装 yum install  -y vsftpd 2.设置开机启动 systemctl enable vsftpd.service 3.启动 systemctl start vsftp ...