欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript Set和Map数据结构

程序员文章站 2022-06-19 16:12:43
JavaScript Set和Map数据结构文章目录JavaScript Set和Map数据结构一.前言二.Set1.成员唯一2.属性方法1. size2.add()3.delete()4.has()5.clear()3.无法按下标查询,修改,排序4.Set的取值1.扩展运算符解构赋值2.forEach遍历3.for...of遍历4.keys()5.values()6.entries()5.Array和Set的对比6.Set的应用1.Set转为数组2.数组去重3.数组的map和filter可以间接用于Se...

JavaScript Set和Map数据结构

一.前言

ES6 提供了两种新的数据结构 Set和Map.Set是集合,Map是字典

二.Set

Set是一个构造函数,用来生成Set数据结构,它类似于数组,但是成员的值都是唯一的,没有重复的

初始化Set可以接受一个数组或类数组对象(或者具有iterable接口的其他数据结构)作为参数,也可以创建一个空的Set

let s1 = new Set();
let s2 = new Set([1, 2, 3, 4]);
let s3 = new Set(document.querySelectorAll('div'));
console.log(s1);//Set(0)
console.log(s2);//Set(4)

1.成员唯一

在Set中成员的值是唯一的,重复的值自动被过滤掉

let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3);//{1, 2, 3, 4}

2.属性方法

1. size

返回成员总数

let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.size);//去重之后,成员总数为4
2.add()

添加某个值,返回Set结构本身(可以链式调用)

let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.add([6,7]))//Set(5) {1, 2, 3, 4, Array(2)}
console.log(s3.add(5));//Set(5) {1, 2, 3, 4, 5}
3.delete()

删除某个值,返回一个bool值,表示是否删除成功

let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.delete(4));//true
4.has()

返回一个bool值,表示该值是否为Set的成员

let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.has(5));//false
5.clear()

清除所有成员,没有返回值

let s3 = new Set([1, 2, 2, 3, 3, 4])
s3.clear();
console.log(s3);//Set(0) {}

3.无法按下标查询,修改,排序

得益于数据结构Set查找更快速更高效,但是也因为数据结构的内部数据是无序的,无法实现按下标查询,修改,排序等操作

var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
console.log(s1[0]);//undefined
console.log(s1['a']);//undefined

4.Set的取值

Set没有类似getter方法,取值怎么取呢?

1.扩展运算符解构赋值
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
var [a, b, c] = [...s1];
console.log(a);//1
console.log(b);//2
console.log(c);//a

var array=[...s1];
console.log(array);//[1, 2, "a", 3, "b"]
2.forEach遍历

使用回调函数遍历每个成员

var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
s1.forEach((val, key, set) => {
    console.log("val", val);
    console.log("key", key);
    console.log("set", set);
});

Set结构的键名就是键值(两者同一个值),因此使用forEach遍历时,第一个参数和第二个参数的值永远都是一样

3.for…of遍历
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1) {
    console.log(val);
}

可以循环遍历出Set中的每一个值

4.keys()

返回键名的遍历器

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

var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.keys()) {
    console.log(val);
}
5.values()

返回键值的遍历器。

var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.values()) {
    console.log(val);
}
//1
//2
//a
//3
//b
6.entries()

返回键值对的遍历器

var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.values()) {
    console.log(val);
}
//[1, 1]
//[2, 2]
//["a", "a"]
//[3, 3]
//["b", "b"]

5.Array和Set的对比

  • ArrayindexOf方法比Sethas方法效率低下
  • Set不含有重复值(可以利用这个特性对数组进行去重)
  • Set通过delete方法删除某个值,而Array只能通过splice,两者的使用方便程度,前者更优.
  • Array的很多新方法map,filter,some,ervery等是Set没有的(但是两者可以互相转换来使用)

6.Set的应用

1.Set转为数组

使用Array.from方法可以将Set结构转为数组

const items = new Set([1, 2, 3, 4, 5])
const array = Array.from(items)
2.数组去重
// 方法一:
var arr1 = [1,2,4,2,3,5,1,3];
var arr2 = [...new Set(arr1)];
console.log(arr2);// [1, 2, 4, 3, 5]

// 方法二:
var arr1 = [1,2,4,2,3,5,1,3];
var arr2 = Array.from(new Set(arr1));
console.log(arr2);// [1, 2, 4, 3, 5]
3.数组的map和filter可以间接用于Set
let s2 = new Set([1, 2, 3, 4]);
s2 = new Set([...s2].map((ele) => {
    return ele * 2;
}));
console.log(s2);//Set(4) {2, 4, 6, 8}


let s2 = new Set([1, 2, 3, 4]);
s2 = new Set([...s2].filter((ele) => {
    return ele > 2;
}));
console.log(s2)//Set(2) {3, 4}
4.实现并集 (Union)、交集 (Intersect) 和差集

并集:2个集合合并,并且去掉重复的

交集:寻找2个集合或多个集合之间相同的成员

差集:所有属于A且不属于B的元素构成的集合,叫做集合A减集合B(或集合A与集合B之差)

(1)并集

let s1 = new Set([1, 2, 3]);
let s2 = new Set([3, 4, 5]);
let union = new Set([...s1, ...s2]);
console.log(union);

(2)交集

let s1 = new Set([1, 2, 3]);
let s2 = new Set([3, 4, 5]);
let intersect = new Set([...s1].filter((ele) => {
    return s2.has(ele);
}));
console.log(intersect);//Set(1) {3}

(3)差集

let s1 = new Set([1, 2, 3]);
let s2 = new Set([3, 4, 5]);
let difference = new Set([...s1].filter((ele) => {
    return !s2.has(ele);
}));
console.log(difference)//Set(2) {1, 2}

三.WeakSet

WeakSet结构与Set类似,也是不重复的值的集合

WeakSet的成员只能是对象(任何具有Iterable接口的对象),不能是其他类型的值(原生js对象不具备这个条件)

const a=[1,2];
new WeakSet(a);// Uncaught TypeError: Invalid value used in weak set

const ws = new WeakSet({ a: 1, b: 2 });
console.log(ws);//object is not iterable (cannot read property Symbol(Symbol.iterator))

上述代码会报错,因为WeakSet只能存储对象或者类似数组的对象

1.WeakSet特点

(1) 成员都是数组或类似数组的对象,若调用add()方法时,传入了非数组和类似数组的对象的参数,就会抛出错误

(2) 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄露.

(3) WeakSet不可迭代,因此不能被用在for...of等循环中.

(4) WeakSet没有size属性

2.WeakSet成员弱引用

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

弱引用:弱引用就是不保证不被垃圾回收机制回收的对象,它拥有比较短暂的生命周期,在垃圾回收机制运行时,只要发现具有弱引用的对象,就会回收它的内存

3.不会引发内存泄漏

因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存.结束使用该值之后,有时忘记取消引用,导致内存无法释放,故而引起内存泄露,WeakSet里面的引用,都不计入垃圾回收机制,所以不存在这个问题,

因此,WeakSEt适合临时存放一组对象,以及存放跟对象绑定的信息,只要对象在外部消息,它在WeakSet里面的引用就会自动消失.

4.使用WeakSet

const ws = new WeakSet();

WeakSet 可以接受一个数组或类似数组的对象作为参数(实际上,任何具有Iterable接口的对象,都可以作为WeakSet的参数)

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
console.log(ws);//WeakSet {Array(2), Array(2)}

注意:a数组的成员成为 WeakSet 的成员,而不是a数组本身。这意味着,数组的成员只能是对象。也就是说,如果我们要传数组的话必须使用[[1,2]]数组将里层对象或数组给包起来.

5.WeakSet的方法

  • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在
const ws = new WeakSet();
const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false

ws.delete(window);
ws.has(window);    // false

四.Map

Map是一个构造函数,用来生成Map数据结构,它类似于对象,也是键值对的集合,但是键可以是非字符串

Map中存储的是key-value形式的键值对,其中key和value可以是任何数据类型的,即对象也可以作为key,Map的出现,就是让各种类型的值都可以当做键,Map提供的是值-值的对应.也就是后端语言常用的数据字典

1.Map和原生Object的区别

(1) Object对象有原型,也就是说它有默认的key值在对象上面,除非我们使用object.create(null)创建一个没有原型的对象

(2) Object对象中,只能把string和symbol作为key值,但是在Map中,key值可以是任何数据类型的(String,Number,Boolean,Undefined,NaN,Null)或对象(Map,Set,Object,Function,Symbol)

(3) 通过Map中的size属性,可以很方便的获取Map的长度,要获取Object长度,只能手动计算

传统 Object 用字符串作键,Object 结构提供了“字符串 — 值”的对应Map 结构提供了“值 — 值”的对应,是一种更完善的 Hash 结构实现如果你需要“键值对”的数据结构,Map 比 Object 更快速 更高效 更合适。

2.Map的使用

初始化 Map 需要一个二维数组,或者直接初始化一个空的 Map

const m = new Map();
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m);//Map(0) {}
console.log(m1);//Map(2) {"name" => "liuqiao", "age" => 27}

3.属性方法

1. size

返回成员总数

const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.size);//2
2.set(key,value)

设置键值对

const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.set("sex","男");
console.log(m1);//Map(3) {"name" => "liuqiao", "age" => 27, "sex" => "男"}
3.get(key)

获取键对应的值

const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.get("name"));//liuqiao
4.has(key)

是否存在某个键

const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.has("name"));//true
5.delete(key)

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

const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.delete("age"));//true
console.log(m1);// {"name" => "liuqiao"}
6.clear()

将Map中的所有元素全部删除

const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.clear();
console.log(m1);//Map(0) {}

4.遍历Map

1.forEach遍历
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.forEach((val, key, map) => {
    console.log("val", val);
    console.log("key", key);
    console.log("map", map);
});
2.for…of遍历
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (const val of m1) {
    console.log(val);//打印的是数组
}

//可以使用如下方式,得到键和值
for (const [key,value] of m1) {
    console.log(key+" "+val);
}
3.keys()
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (let key of m1.keys()) {
    console.log(key)
}
//name,age
4.values()
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (let value of m1.values()) {
    console.log(value)
}
//liuqiao,27
5.entries()
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (const val of m1.entries()) {
    console.log(val);//打印的是数组
}

//可以使用如下方式,得到键和值
for (const [key,value] of m1.entries()) {
    console.log(key+" "+val);
}

实际上,你会发现,for...of...遍历map等同于使用map.entries()

5.数据类型转换

1.Map转为数组
const map=new Map();
const arr=[...map];
2.数组转为Map
let arr=[];
let map=new Map(arr);
3.Map转为对象
const obj={};
for (let [k, v] of map) {
  obj[k] = v
}
4.对象转为Map
let map=new Map();
for( let k of Object.keys(obj){
  map.set(k,obj[k])
}

6.重复键的情况

1.引用类型情况
const m1 = new Map();
m1.set(['a'], 5);
m1.set(['a'], 6);
console.log(m1);//Map(2) {Array(1) => 5, Array(1) => 6}
console.log(m1.get(['a']));//undefined

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

Map的键本质上还是跟内存地址绑定的,只要内存地址不一样,就会看成2个不同的键

2.值类型情况
const m1 = new Map();
m1.set('name','zhangsan');
m1.set('name','lisi');
console.log(m1);//Map(1) {"name" => "lisi"}

值类型就不会有引用地址的问题,如果键名有重复的,则以后面的准,会把已经存在的替换掉

五.WeakMap

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

1.特点

  • 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
  • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
  • 不能遍历,方法有 getsethasdelete

2.设计目的

WeakMap设计目的在于,有时我们想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用

const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
  [e1, 'foo 元素'],
  [e2, 'bar 元素'],
];

e1和e2是两个对象,我们通过arr数组对这两个对象添加文字说明,这就形成了arr对e1和e2的引用

WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。

注意:WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。

const wm = new WeakMap();
let key = {};
let obj = {foo: 1};

wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}

3.WeakMap使用

const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"

4应用场景

在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除

<template>
  <div id="app">
    <div class="info-item" v-for="[label, value] in infoMap" :key="value">
      <span>{{label}}</span>
      <span>{{value}}</span>
    </div>
  </div>
</template>

data: () => ({
  info: {},
  infoMap: {}
}),
mounted () {
  this.info = {
    name: 'jack',
    sex: '男',
    age: '28',
    phone: '13888888888',
    address: '广东省广州市',
    duty: '总经理'
  }
  const mapKeys = ['姓名', '性别', '年龄', '电话', '家庭地址', '身份']
  const result = new Map()
  let i = 0
  for (const key in this.info) {
    result.set(mapKeys[i], this.info[key])
    i++
  }
  this.infoMap = result
}

六.总结

1.Set

  • 是一种叫做集合的数据结构(ES6新增的)
  • 成员唯一、无序且不重复
  • [value, value],键值与键名是一致的(或者说只有键值,没有键名)
  • 允许储存任何类型的唯一值,无论是原始值或者是对象引用
  • 可以遍历,方法有:adddeletehasclear

2.WeakSet

  • 成员都是对象
  • 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏
  • 不能遍历,方法有 adddeletehas

3.Map

  • 是一种类似于字典的数据结构,本质上是键值对的集合
  • 可以遍历,可以跟各种数据格式转换
  • 操作方法有:setgethasdeleteclear

4.WeakMap

  • 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
  • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
  • 不能遍历,方法有 getsethasdelete

本文地址:https://blog.csdn.net/liuqiao0327/article/details/107901064

相关标签: JavaScript