JS 怎么理解ES6新增Set、Map两种数据结构?

简介: JS 怎么理解ES6新增Set、Map两种数据结构?

一、前言

Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构


什么是集合?什么又是字典?


集合是由一堆无序的、相关联的,且不重复的内存结构【数学中称为元素】组成的组合


字典是一些元素的集合。每个元素有一个称作key 的域,不同元素的key 各不相同


区别?


共同点:集合、字典都可以存储不重复的值

不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储


下面对这两种数据结构进行详解


二、Set

1.Set数据结构定义

用于存储任何类型的唯一值,无论是基本类型还是对象引用。


2.Set数据结构的特性

(1) 只能保存值没有键名

(2) 严格类型检测如字符串数字不等于数值型数字

(3) 值是唯一的

(4) 遍历顺序是添加的顺序,方便保存回调函数


3.Set数据结构的基本使用

(1) add: 添加元素


使用 add 添加元素,不允许重复添加相同的值

let set = new Set()
set.add(1).add(1).add(1);
console.log(set.values());  // [1]

Set严格区分值类型 1 和 '1' 属于两个不同的值

let set = new Set();
set.add(1);
set.add('1');
console.log(set.values());  // [1,'1']

(2) has: 检测元素是否存在,返回布尔值,存在返回 true

let set = new Set();
set.add(1);
set.add('1');
console.log(set.values());  // [1,'1']

(3) size 返回 Set 长度

let set = new Set(['zs', 'lisi']);
console.log(set.size); // 2

(4) delete: 删除单个元素方法,返回值为boolean类型

let set = new Set(['zs', 'lisi']);
set.delete('zs');
console.log(set.values()); // ['lisi']

(5) clear: 清空所有成员,没有返回值

let set = new Set(['zs', 'lisi']);
set.clear();
console.log(set.values()); // []

(6) 数组转换


可以使用 点语法 或 Array.form 静态方法将Set类型转为数组,这样就可以使用数组处理函数了

let set = new Set(['zs', 'lisi']);
console.log([...set]); //  ['zs', 'lisi']
console.log(Array.from(set)); //  ['zs', 'lisi']

4.Set遍历数据

(1). forEach():使用回调函数遍历每个成员

let set = new Set(['zs', 'lisi'])
set.forEach((item, index) => {
    console.log(item, index); // zs zs  lisi lisi
});

(2). keys():返回键名的遍历器

let set = new Set(['zs', 'lisi']);
for (let i of set.keys()) {
    console.log(i); // zs lisi
}

(3). values():返回键值的遍历器

let set = new Set(['zs', 'lisi']);
for (let i of set.values()) {
    console.log(i); // zs lisi
}

(4) entries():返回键值对的遍历器

let set = new Set(['zs', 'lisi']);
for (let i of set.entries()) {
    console.log(i); // ['zs','zs'] ['lisi','lisi']
}

5.Set 的使用场景

(1). 利用Set实现去重


实现字符串去重

let str = 'helloworld';
console.log([...new Set(str)]); // ['h', 'e', 'l', 'o', 'w', 'r', 'd']

实现数组去重

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

(2).实现并集,交集,差集

            let a = new Set([1, 2, 3]);
            let b = new Set([2, 3, 4]);
            // 并集
            let bingji = new Set([...a, ...b]);
            console.log(bingji.values()); // [1,2,3,4]
            // 交集
            let jiaoji = new Set([...a].filter((x) => b.has(x)));
            console.log(jiaoji.values()); // [2,3]
            // (a 相对于 b 的)差集
            let chaji = new Set([...a].filter((x) => !b.has(x)));
            console.log(chaji.values()); // [1]

6.WeakSet的使用

WeakSet结构同样不会存储重复的值,它的成员必须只能是对象类型的值


WeakSet的特性  


  (1). 垃圾回收不考虑WeakSet,即被WeakSet引用时引用计数器不加一,所以对象不被引用时不管WeakSet是否在使用都将删除

  (2). 因为WeakSet 是弱引用,由于其他地方操作成员可能会不存在,所以不可以进行forEach( )遍历等操作

  (3). 也是因为弱引用,WeakSet 结构没有keys( ),values( ),entries( )等方法和size属性

  (4). 因为是弱引用所以当外部引用删除时,希望自动删除数据时使用 WeakSet


  (5). WeakSet没有size属性


(1) WeakSet的值必须为对象类型,若为其他类型会报错

            // 正确声明
            let weakSet = new WeakSet([
                [1, 2],
                [3, 4],
            ]);
            // 错误声明
            let weakSet1 = new WeakSet([1, 2]); //  Invalid value used in weak set

(2) 当使用WeakSet保存 DOM 节点时,当DOM节点删除时,WeakSet会自动删除对DOM节点的引用,不用担心dom节点从文档中移除产生内存泄漏的问题


7.垃圾回收机制

WeaSet保存的对象不会增加引用计数器,如果一个对象不被引用了会自动删除。

        let set = new WeakSet();
        let arr = ['zs', 'lisi'];
        set.add(arr, 123);
        arr = null;
        console.log(set);
        setTimeout(() => {
            console.log(set);
        }, 1000);

三、Map

1.Map数据结构定义

Map是一组键值对的结构,用于解决以往不能用对象做为键的问题


2.Map数据结构的特性

(1) 具有极快的查找速度

(2) 函数、对象、基本类型都可以作为键或值


3.Map数据结构的基本使用

(1) set 添加元素,支持链式操作

            let map = new Map([]);
            map.set('name', 'zs');
            map.set('age', '18');
            map.set('hobby', [1, 2, 3]).set('a', 'b');
            console.log(map);

c9ad56ab96ac44e097ac1d69b57b6caf.png

(2).get 获取元素

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            console.log(map.get('name')); // 'zs'

(3).size 获取数量,返回map长度

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            console.log(map.size); // 2

(4).has 利用key值检测是否存在,返回布尔值,存在返回true

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            console.log(map.has('name')); // true
            console.log(map.has('hobby')); // false

(5).delete方法 删除单个元素

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            map.delete('name');
            console.log(map.has('name')); // false

(6).clear方法 清空map所有元素

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            map.clear('name');
            console.log(map.size); // 0

(7).数据转换


可以使用 展开语法 或 Array.form 静态方法将Map类型转为数组,这样就可以使用数组处理函数了

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            console.log([...map]);
            console.log(Array.from(map));

c642e295fb01498ca867c5114135ddcd.png


4.Map遍历数据

keys():返回键名的遍历器

values():返回键值的遍历器

entries():返回所有成员的遍历器

forEach():遍历 Map 的所有成员

            let map = new Map([
                ['name', 'zs'],
                ['age', 18],
            ]);
            map.forEach((value, key) => {
                console.log(key, value); // name zs  // age 18
            });
            for (let i of map.keys()) {
                console.log(i); // name age
            }
            for (let value of map.values()) {
                console.log(value); // zs 18
            }
            for (let entries of map.entries()) {
                console.log(entries); // ['name','zs'] ['age',18]
            }
            // 等同于上面的 map.entries()
            for (let [key, value] of map.entries()) {
                console.log(key, value); // name zs // age 18
            }

5.Map的使用场景

(1) 当页面中有多个表单元素需要进行数据交互时,可以使用Map来存储表单数据

            const formData = new Map();
            formData.set('username', 'zs');
            formData.set('password', '123456');
            const username = formData.get('username'); //zs
            const password = formData.get('password'); //123456

通过使用Map来管理数据,可以使代码更加简洁和易于维护


6.WeakMap的使用

WeakMap 对象是一组键/值对的集


(1) 键名必须是对象

(2) WeaMap对键名是弱引用的,键值是正常引用

(3) 垃圾回收不考虑WeaMap的键名,不会改变引用计数器,键在其他地方不被引用时即删除

(4) 因为WeakMap 是弱引用,由于其他地方操作成员可能会不存在,所以不可以进行forEach( )遍历等操作

(5) 也是因为弱引用,WeaMap 结构没有keys( ),values( ),entries( )等方法和 size 属性

(6) 当键的外部引用删除时,希望自动删除数据时使用 WeakMap


(1) WeakMap的值必须为对象类型,若为其他类型会报错

            let weakMap = new WeakMap();
            // 正确声明
            weakMap.set({ name: 'zs' }, 'lisi');
            // 错误声明
            weakMap.set(1, 2); // Invalid value used as weak map key
            // at WeakMap.set (<anonymous>)
            weakMap.set(null, 1); //  Invalid value used as weak map key
            // at WeakMap.set (<anonymous>)

(2) 利用WeakMap 保存 DOM节点

    <body>
        <div>zs</div>
        <div>lisi</div>
    </body>
    <script>
        const map = new WeakMap();
        document.querySelectorAll('div').forEach((item) => map.set(item, item.innerHTML));
        console.log(map); //WeakMap {div => "zs", div => "lisi"}
    </script>

7.垃圾回收

WakeMap的键名对象不会增加引用计数器,如果一个对象不被引用了会自动删除。


下例当 obj 删除时内存即清除,因为WeakMap是弱引用不会产生引用计数

        let map = new WeakMap();
        let obj = { name: 'zs' };
        map.set(obj, 'zs');
        obj = null;
        console.log(map);
        setTimeout(() => {
            console.log(map);
        }, 1000);
目录
相关文章
|
24天前
|
编译器 容器
哈希表模拟封装unordered_map和unordered_set
哈希表模拟封装unordered_map和unordered_set
|
24天前
|
编译器 测试技术 计算机视觉
红黑树模拟封装map和set
红黑树模拟封装map和set
|
3月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
87 18
你对Collection中Set、List、Map理解?
|
3月前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
81 20
|
4月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
76 3
【C++】map、set基本用法
|
4月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
43 5
|
4月前
|
JavaScript 前端开发 安全
ECMAScript 6(以下简称 ES6)的出现为 JavaScript 带来了许多新的特性和改进,其中 let 和 const 是两个非常重要的关键字。
ES6 引入了 `let` 和 `const` 关键字,为 JavaScript 的变量管理带来了革新。`let` 提供了块级作用域和暂存死区特性,避免变量污染,增强代码可读性和安全性;`const` 用于声明不可重新赋值的常量,但允许对象和数组的内部修改。两者在循环、函数内部及复杂项目中广泛应用,有助于实现不可变数据结构,提升代码质量。
51 5
|
4月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
393 9
|
4月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
66 1
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
160 77