与数学中的集合概念类似,集合由一组无序的元素组成,且集合中的每个元素都是唯一存在的。可以回顾一下中学数学中集合的概念,我们这里所要定义的集合也具有空集(即集合的内容为空)、交集、并集、差集、子集的特性。

  在ES6中,原生的Set类已经实现了集合的全部特性,稍后我们会介绍它的用法。

  我们使用JavaSctipt的对象来表示集合,下面是集合类的主要实现方法:

class Set {
constructor () {
this.items = {};
} add (value) { // 向集合中添加元素
if (!this.has(value)) {
this.items[value] = value;
return true;
}
return false;
} delete (value) { // 从集合中删除对应的元素
if (this.has(value)) {
delete this.items[value];
return true;
}
return false;
} has (value) { // 判断给定的元素在集合中是否存在
return this.items.hasOwnProperty(value);
} clear() { // 清空集合内容
this.items = {};
} size () { // 获取集合的长度
return Object.keys(this.items).length;
} values () { // 返回集合中所有元素的内容
return Object.values(this.items);
}
}

  在使用JavaScript对象{ }来表示集合时,我们可以像操作数组一样通过[ ]来设置和获取集合内元素的值。通过这种方式,在设置集合元素的值时,如果元素不存在,则创建一个新元素,如果元素存在,则修改对应的值;在获取集合元素的值时,如果元素存在,则返回对应的值,如果元素不存在,则返回undefined。此外,JavaScript对象还提供了一些基础方法以方便我们对集合进行一些操作,例如hasOwenProperty()方法返回一个表明对象是否具有特定属性的布尔值,Object.keys()方法返回指定对象的所有属性名称的数组,Object.values()方法方法指定对象的所有属性值的数组。

  上述代码很简单,这里就不再详细解释了。下面是一些测试用例和测试结果:

let set = new Set();
set.add(1);
console.log(set.values()); // [ 1 ]
console.log(set.has(1)); // true
console.log(set.size()); // set.add(2);
console.log(set.values()); // [ 1, 2 ]
console.log(set.has(2)); // true
console.log(set.size()); // set.delete(1);
console.log(set.values()); // [ 2 ] set.delete(2);
console.log(set.values()); // []

  下面我们来看看集合的数学运算:并集、交集、差集、子集。

并集

  对于给定的两个集合,并集返回一个包含两个集合中所有元素的新集合。示意图如下:

  并集的实现代码:

union (otherSet) { // 并集
let unionSet = new Set();
this.values().forEach(value => unionSet.add(value));
otherSet.values().forEach(value => unionSet.add(value));
return unionSet;
}

  首先遍历第一个集合,将所有的元素添加到新集合中,然后再遍历第二个集合,将所有的元素添加到新集合中。然后返回新集合。不用担心会添加重复的元素,因为集合的add()方法会自动排除掉已添加的元素。

  测试用例及结果:

let setA = new Set();
setA.add("first");
setA.add("second");
setA.add("third"); let setB = new Set();
setB.add("third");
setB.add("fourth");
setB.add("fifth");
setB.add("sixth"); console.log(setA.union(setB).values()); // [ 'first', 'second', 'third', 'fourth', 'fifth', 'sixth' ]

交集

  对于给定的两个集合,交集返回一个包含两个集合中共有元素的新集合。示意图如下:

  交集的实现代码:

intersection (otherSet) { // 交集
let intersectionSet = new Set();
this.values().forEach(value => {
if (otherSet.has(value)) intersectionSet.add(value);
});
return intersectionSet;
}

  遍历第一个集合,如果元素出现在第二个集合中,则将它添加到新集合。然后返回新集合。

  测试用例及结果:

let setA = new Set();
setA.add("first");
setA.add("second");
setA.add("third"); let setB = new Set();
setB.add("second");
setB.add("third");
setB.add("fourth"); console.log(setA.intersection(setB).values()); // [ 'second', 'third' ]

差集

  对于给定的两个集合,差集返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。示意图如下:

  差集的实现代码:

difference (otherSet) { // 差集
let differenceSet = new Set();
this.values().forEach(value => {
if (!otherSet.has(value)) differenceSet.add(value);
});
return differenceSet;
}

  遍历第一个集合,如果元素没有出现在第二个集合中,则将它添加到新集合。然后返回新集合。

  测试用例及结果:

let setA = new Set();
setA.add("first");
setA.add("second");
setA.add("third"); let setB = new Set();
setB.add("second");
setB.add("third");
setB.add("fourth"); console.log(setA.difference(setB).values()); // [ 'first' ]

子集

  验证一个给定集合是否是另一个集合的子集,即判断给定的集合中的所有元素是否都存在于另一个集合中,如果是,则这个集合就是另一个集合的子集,反之则不是。示意图如下:

  子集的实现代码:

subset (otherSet) { // 子集
if (this.size() > otherSet.size()) return false; let isSubset = true;
this.values().every(value => {
if (!otherSet.has(value)) {
isSubset = false;
return false;
}
return true;
}); return isSubset;
}

  如果集合A比集合B的长度大,则直接返回false,因为这种情况A不可能是B的子集。然后使用every()函数遍历集合A的所有元素,一旦碰到其中的元素没有在集合B中出现,则直接返回false,并终止遍历。这里我们没有使用forEach来遍历集合A,是因为你无法根据某个条件来终止forEach循环。考虑下面这种情况:

var arr = ["first", "second", "third", "fourth"];
arr.forEach(item => {
if(item === "third") return true;
console.log(item);
});

  输出结果是:

first
second
fourth

  很显然,这里的return true语句并不能退出forEach循环,它只能保证本次循环中余下的语句不被执行,而接下来其它的元素还是会被遍历到。

  在我们的subset()方法中,如果使用forEach语句,每一次都会遍历集合中的所有元素,如果遇到其中的元素没有在集合B中出现,就将isSubset变量的值设置为false,但并不能退出forEach,isSubset变量的值可能会被多次覆盖。为了提高执行效率,推荐使用every()函数,它会遍历集合中的元素,直到其中一个返回结果为false,就终止遍历,并返回false,否则就遍历所有的元素并返回true。有关every()函数的详细介绍可以看这里。与every()函数功能相似还有一个some()函数,它在遍历集合的过程中,遇到返回结果为true时就终止遍历,具体内容可以看这里

  差集的测试用例及结果:

let setA = new Set();
setA.add("first");
setA.add("second"); let setB = new Set();
setB.add("first");
setB.add("second");
setB.add("third"); let setC = new Set();
setC.add("second");
setC.add("third");
setC.add("fourth"); console.log(setA.subset(setB)); // true
console.log(setA.subset(setC)); // false

  文章的开头说过,ES6提供了原生的Set类,让我们来看看它的一些使用方法:

let set = new Set();
set.add(1);
set.add(2);
set.add(3);
console.log(set.values()); // [Set Iterator] { 1, 2, 3 }
console.log(set.has(1)); // true
console.log(set.size); // set.delete(1);
console.log(set.values()); // [Set Iterator] { 2, 3 } set.clear();
console.log(set.values()); // [Set Iterator] { }

  和前面我们自定义的Set类稍微有一点不同,values()方法返回的不是一个数组,而是Iterator迭代器。另一个就是这里的size是一个属性而不是方法,其它部分都和我们前面定义的Set类相同。由于ES6的Set类不包含对集合的数学运算,我们可以按照前面我们提供的方法来对其进行扩充。有关ES6的Set类的详细介绍可以看查看这里

  下一章我们将介绍如何用JavaScript来实现字典和散列表。

JavaScript数据结构——集合的实现与应用的更多相关文章

  1. JavaScript数据结构——集合、字典和散列表

    集合.字典和散列表都可以存储不重复的值. 在集合中,我们感兴趣的是每个值本身,并把它当作主要元素.在字典和散列表中,我们用 [键,值] 的形式来存储数据. 集合(Set 类):[值,值]对,是一组由无 ...

  2. 学习javascript数据结构(三)——集合

    前言 总括: 本文讲解了数据结构中的[集合]概念,并使用javascript实现了集合. 原文博客地址:学习javascript数据结构(三)--集合 知乎专栏&&简书专题:前端进击者 ...

  3. 为什么我要放弃javaScript数据结构与算法(第六章)—— 集合

    前面已经学习了数组(列表).栈.队列和链表等顺序数据结构.这一章,我们要学习集合,这是一种不允许值重复的顺序数据结构. 本章可以学习到,如何添加和移除值,如何搜索值是否存在,也可以学习如何进行并集.交 ...

  4. JavaScript数据结构与算法-集合练习

    集合的实现 function Set () { this.dataStore = []; this.add = add; this.remove = remove; this.size = size; ...

  5. 学习javascript数据结构(二)——链表

    前言 人生总是直向前行走,从不留下什么. 原文地址:学习javascript数据结构(二)--链表 博主博客地址:Damonare的个人博客 正文 链表简介 上一篇博客-学习javascript数据结 ...

  6. 学习javascript数据结构(一)——栈和队列

    前言 只要你不计较得失,人生还有什么不能想法子克服的. 原文地址:学习javascript数据结构(一)--栈和队列 博主博客地址:Damonare的个人博客 几乎所有的编程语言都原生支持数组类型,因 ...

  7. JavaScript数据结构——链表

    链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 好处:可以添加或移除任意项,它会按需扩容 ...

  8. JavaScript数据结构——栈和队列

    栈:后进先出(LIFO)的有序集合 队列:先进先出(FIFO)的有序集合 --------------------------------------------------------------- ...

  9. 学习javascript数据结构(四)——树

    前言 总括: 本文讲解了数据结构中的[树]的概念,尽可能通俗易懂的解释树这种数据结构的概念,使用javascript实现了树,如有纰漏,欢迎批评指正. 原文博客地址:学习javascript数据结构( ...

随机推荐

  1. JavaWeb入门_模仿天猫整站Tmall_SSM实践项目

    Tmall_SSM 技术栈 Spring MVC+ Mybatis + Spring + Jsp + Tomcat , 是 Java Web 入门非常好的练手项目 效果展示: 模仿天猫前台 模仿天猫后 ...

  2. 模拟实现 Tomcat 的核心模块:NIO,HTTP,容器和集群

    如果你想看 Tomcat 源码但又无从入手,不妨从这个项目开始,代码量不多,但包含了 Tomcat 的核心处理流程,并且源码中有相当丰富的注释.相信通过此项目你能了解: NIO 基本编程.HTTP 协 ...

  3. C++用EGE简单实现别踩白块游戏

    本项目已开源:https://github.com/wmpscc/AvoidBlank 关于EGE 介绍:EGE(Easy Graphics Engine),是windows下的简易绘图库,是一个类似 ...

  4. Windows使用Cmder

    Windows使用Cmder cmder是一个增强型命令行工具, 不仅可以使用Windows下的所有命令, 更爽的是可以使用linux的命令, shell命令. 下载 官网地址: cmder官网 下载 ...

  5. 如何安装xenserver

    xenserver安装 选择键盘 是否同意协议 清理磁盘 选择本地磁盘安装 选择本地镜像文件 输入管理密码 配置IP地址 配置DNS服务器地址 选择地点 配置NTP服务器地址 开始安装 安装完成

  6. 渗透测试工具SQLmap

    一.简介 SQLmap 是一款用 Python 编写的开源渗透测试工具,用来自动检测和利用 SQL 注入漏洞. 二.Windows 下安装 2.1 安装 Python 环境 注:Python 3.0会 ...

  7. JAVA String类型的一些小操作

    String类型是否包含某个String类型的函数:源字符串.contains(包含字符串)  返回值为:boolean类型(true或false) String类型把某个字符替换成另一个字符:源字符 ...

  8. CSU 1320:Scoop water(卡特兰数)

    http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1320 题意:……是舀的时候里面必须要有1L,而不是舀完必须要有1L. 思路:才知道是卡特兰数. 这 ...

  9. C语言实现—学生成绩管理系统(Linux下运行)

    开发环境:Linux 开发语言:C 相关文件下载: 码云:https://gitee.com/ikaros-521/c_project/tree/master/%E5%AD%A6%E7%94%9F%E ...

  10. ZigBee按键中断

    何为按键中断? 在了解按键中断之前,我们先来了解一下什么是中断?中断就是程序执行当前代码,当前任务的时候: 突然有自身函数或外部的影响,而使程序执行到别的任务再回来. 举个栗子: 当你在做饭的时候,电 ...