数组与汇合的区别

Set 是 ES6 的新增的数据结构,和数组一样,也用来存储值

大多数人认为二者最大的区别是汇合存值的唯一性

但这其实远不足以使汇合闻名,真正让汇合成为一种新数据结构的是其另一长处

汇合在查看值是否存在时要远超数组

上面这段代码对汇合的 has 办法与数组的 includes 办法进行了测试

const fun = (n) => {  const arr = new Array(n).fill(0).map((_, i) => i)  const set = new Set(arr)  console.time('set')  for (let i = 0; i < n; i++) {    set.has(n + 1)  }  console.timeEnd('set')  console.time('arr')  for (let i = 0; i < n; i++) {    arr.includes(n + 1)  }  console.timeEnd('arr')}fun(10 ** 2)// set: 0.012939453125 ms// arr: 0.01904296875 msfun(10 ** 3)// set: 0.058837890625 ms// arr: 1.070068359375 msfun(10 ** 4)// set: 0.38818359375 ms// arr: 105.847900390625 msfun(10 ** 5)// set: 5.6337890625 ms// arr: 592.424072265625 ms
如果掘友复制下面的代码间接执行失去的后果可能与正文相差甚远。这是因为浏览器在频繁解决数组时,会对其进行优化。以上的数据每次仅执行一个 fun 时得出的,只是为了不便写在了一起。

基于以上长处,在不须要数字索引检索的场合,大多时候都是用汇合来存储数据

然而汇合是 ES6 才推出的数据结构,一些库为了兼容较老的浏览器,并不能间接应用汇合,须要本人去实现

接下来介绍一下如何实现汇合

汇合的实现

咱们晓得,对象在查看其属性是否存在时也很快,而且对象的属性也具备唯一性

所以咱们应用对象来实现汇合,把汇合的值作为对象的属性存储起来

因为对象的属性会被转换成字符串,1'1' 应该是不同的,所以咱们采纳两个对象来辨别数字值和字符串值,而后对 nullundefined 也做一下非凡的解决

上面代码实现了汇合的性能

function MySet(iterator) {  this.stringSet = {}  this.numberSet = {}  this.__undefined__ = false  this.__null__ = false  iterator.forEach((value) => {    this.add(value)  })}// 增加元素MySet.prototype.add = function (value) {  if (typeof value === 'string') {    this.stringSet[value] = true  } else if (typeof value === 'number') {    this.numberSet[value] = true  } else if (value === undefined) {    this.__undefined__ = true  } else if (value === null) {    this.__null__ = true  }}// 删除元素MySet.prototype.delete = function (value) {  if (typeof value === 'string') {    this.stringSet[value] = false  } else if (typeof value === 'number') {    delete this.numberSet[value]  } else if (value === undefined) {    this.__undefined__ = false  } else if (value === null) {    this.__null__ = false  }}// 检测元素是否存在MySet.prototype.has = function (value) {  if (typeof value === 'string') {    return !!this.stringSet[value]  } else if (typeof value === 'number') {    return !!this.numberSet[value]  } else if (value === undefined) {    return this.__undefined__  } else if (value === null) {    return this.__null__  }}

有时也须要遍历汇合,所以也实现一个 values 办法

MySet.prototype.values = function () {  var arr = []  if (this.__null__) arr.push(null)  if (this.__undefined__) arr.push(undefined)  for (var num in this.numberSet) {    arr.push(parseFloat(num))  }  for (var str in this.stringSet) {    arr.push(str)  }}

至此咱们的汇合就实现实现了

与 ES6 的汇合有两点不同

  • values 办法应该按元素的插入程序返回元素的迭代器,为了不便咱们就只返回了一个数组,也与插入程序无关。
  • 汇合应该容许插入任何数据,咱们的汇合只能插入根底数据类型。

咱们应用汇合很少会依赖他按序遍历的性能,当咱们的遍历须要依赖肯定程序时,理当换用数组来操作。真要实现按序遍历也就是再内置一个数组记录程序,但在删除元素时就会更加耗时。

至于汇合寄存对象的性能,想要实现一个通用的 转换对象为根底类型的办法 十分麻烦,应用 JSON.stringify 又会呈现正则、工夫、克隆对象的问题,索性就不实现了。

取而代之咱们倡议在应用时应该为每个对象定义一个自增的 id 属性,汇合仅存储操作 id

至于 ES6 的汇合是如何实现以上性能的,咱们晓得 js 的底层是 C++
C++ 中有一个取地址操作,能够轻易的将任何数据类型与数字地址互转,同时还确保了唯一性。
对于数字的增加删除,能够轻松实现按序遍历的要求

结语

如果文中有不了解或不谨严的中央,欢送评论发问。

如果喜爱或有所帮忙,心愿能点赞关注,激励一下作者。