03月07, 2019

JS基础(4)——数据结构(3)——集合

集合(set)是在 ECMAScript 6 中引入的一种数据结构,用于表示唯一的值。所以这种数据结构里面不能包含重复的值。

接下来这一小节,就让我们具体来看一下这种新的数据结构。

4-3-1 什么是集合

在 ECMAScript 6 标准制定以前,可选的数据结构类型有限,可以说只有数组这种数据结构。数组使用的是数值型索引,经常被用来模拟队列和栈的行为。

但是如果需要使用非数值型索引,就会用非数组对象创建所需的数据结构,而这就是 Set 集合与后面一节要介绍的 Map 映射的早期实现。

Set 集合是一种无重复元素的列表,这是这种数据结构最大的一个特点。

4-3-2 创建集合

要创建一个集合,方法很简单,直接使用 new 就可以创建一个 Set 对象。如果想要集合在创建时就包含初始值,那么我们可以传入一个数组进去。

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

4-3-3 给集合添加值

使用add()方法可以给一个集合添加值,由于调用add()方法以后返回的又是一个 Set 对象,所以我们能连续调用add()方法进行值的添加,这种像链条一样的方法调用方式被称为链式调用。

let s1 = new Set();
s1.add(1);
console.log(s1); // Set { 1 }
s1.add(2).add(3).add(4);
console.log(s1);
// Set { 1, 2, 3, 4 }

我们还可以直接将一个数组传入add()方法里面

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

但是需要注意的是建立 Set 对象时传入数组与调用add()方法时传入数组是效果是不一样,区别如下:

建立 Set 对象时传入数组,数组每一项成为 Set 对象的一个元素

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

调用add()方法时传入数组,就是作为 Set 对象的一个元素

let s1 = new Set();
s1.add([1,2,3]);
console.log(s1); // Set { [ 1, 2, 3 ] }
console.log(s1.size); // 1

在 Set 对象中,不能够添加相同的元素,这是很重要的一个特性

let s1 = new Set();
s1.add(1).add(2).add(2).add(3);
console.log(s1);
// Set { 1, 2, 3 }

4-3-4 集合相关属性和方法

1. 用size属性获取元素个数

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

2. 使用has()方法来查看一个集合中是否包含某一个值

let s1 = new Set([1,2,3]);
console.log(s1.has(1)); // true
console.log(s1.has("1")); // false

3. 删除集合值

使用delete()方法删除 Set 对象里面的某一个元素

let s1 = new Set([1,2,3]);
s1.delete(2);
console.log(s1); // Set { 1, 3 }
// 没有的元素也不会报错
s1.delete("2");
console.log(s1); // Set { 1, 3 }

如果要一次性删除所有的元素,可以使用clear()方法

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

4-3-5 遍历集合

集合也是可以枚举的,我们同样可以使用for-of来对集合进行遍历,如下:

let s = new Set([1,2,3,4,5]);
for(let i of s){
    console.log(i);
}
// 1
// 2
// 3
// 4
// 5

或者通过forEach来进行遍历,示例如下:

//使用 forEach 进行遍历
let s = new Set([1,2,3,4,5]);
s.forEach(ele => console.log(ele));
// 1
// 2
// 3
// 4
// 5

除此之外,我们也可以使用集合里面自带的keys()values()以及entries()方法来对集合进行遍历。顺便要说一下的是,在集合里面键和值是相同的

keys()方法遍历集合的键:

let s = new Set(["Bill","Lucy","David"]);
for(let i of s.keys()){
    console.log(i);
}
// Bill
// Lucy
// David

values()方法遍历集合的值:

let s = new Set(["Bill","Lucy","David"]);
for(let i of s.values()){
    console.log(i);
}
// Bill
// Lucy
// David

entries()方法同时遍历集合的键与值:

let s = new Set(["Bill","Lucy","David"]);
for(let i of s.entries()){
    console.log(i);
}
// [ 'Bill', 'Bill' ]
// [ 'Lucy', 'Lucy' ]
// [ 'David', 'David' ]

4-3-6 集合转数组

将集合转为数组,最快的方法就是使用前面所讲过的扩展运算符,如下:

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

除此之外,我们还可以使用 Array 类的静态方法from()来进行转换

let s1 = new Set([1,2,3]);
console.log(s1); // Set { 1, 2, 3 }
let arr = Array.from(s1);
console.log(arr); // [ 1, 2, 3 ]

前面我们有提到过,Set 对象里面是不能够存放相同的元素的,利用这个特性,我们可以快速的为数组去重,如下:

let arr = [1,2,2,3,4,3,1,6,7,3,5,7];
let s1 = new Set(arr);
let arr2 = [...s1];
console.log(arr2); // [ 1, 2, 3, 4, 6, 7, 5 ]

4-3-7 弱集合(扩展)

当对象添加到集合中时,只要集合存在,它们就一直存储在集合。即使对象的引用被删除了也依然如此,我们来看下面的这个例子:

let arr = [1,2,3];
let s = new Set(arr);
arr = null; // 删除arr数组的指向
console.log(s); // Set { 1, 2, 3 } 数组依然存在于集合中
console.log(arr); // null

可以看到,这里我们删除了对数组的引用,但是该数组依然存在,只不过里面的值为 null,这样的话垃圾回收就不会不会回收这个数组,从而可能会引起内存泄漏

什么是内存泄漏?

一个程序里面保留着已经不能在内存中访问的值时,就会发生内存泄露,也就是说占着空间却没用,造成内存的浪费。

例如:

let arr = [1,2,3];
arr = null;

断开了 arr 对 1,2,3 的引用,现在 1,2,3 在内存里面已经是垃圾了。内存泄露会逐渐减少全部可用内存,导致程序和系统的速度变慢甚至崩溃。

那么怎样才能清空这些没用的数据呢?例如上例中的 1,2,3。事实上在 JavaScript 中采用的是动态内存管理技术,比如垃圾回收机制,会自动从内存中删除不再被程序需要的东西。而有些编程语言,例如 C++,则是需要程序员手动的管理内存,在某些东西完成任务之后,将其从内存中删除。

那么,集合的问题就在于即使失去了引用,也不会被垃圾回收,这个时候我们可以使用弱集合来避免这种状况。创建弱集合使用new运算符和WeakSet()构造函数,如下:

let weak = new WeakSet();

由于弱集合要解决的问题是引用数据变为垃圾时无法被回收的问题,所以弱集合无法添加基本数据类型,也就是说无法像集合那样添加简单值进去。

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

除了这个限制以外,弱集合和普通集合还有一些细微的区别,例如无法在创建弱集合时传入一个数组进行初始化。

let arr = [1,2,3,4,5];
let weak = new WeakSet(arr);
// TypeError: Invalid value used in weak set
// 无法在创建弱集合时传入一个数组进行初始化

不过弱集合也拥有has()add()delete()等方法。还需要注意一点的是,弱集合是对对象的弱引用,所以不能访问对象里面的值列表。这使得弱集合看上去像是空的,但是并不是空的,证明如下:

let weak = new WeakSet();
let arr = [1,2,3];
weak.add(arr);
console.log(weak); // WeakSet {}
console.log(weak.has(arr)); // true

本文链接:http://www.yanhongzhi.com/post/js-basis-10.html

-- EOF --

Comments