使用Set数据类型加速JavaScript

该材料的作者(我们今天将发表其翻译)说,他相信许多JavaScript开发人员主要使用诸如NumberStringObjectArrayBoolean类的数据类型。 在大多数情况下,这就足够了。 但是,如果您需要使代码尽可能快且可扩展,那么使用这些数据类型并不总是合理的。



在本文中,我们将讨论如何使用Set数据类型(该数据类型提供了使用唯一值集合的功能)使代码更快的方法。 对于大型项目的代码尤其如此。 ArraySet类型有很多共同点,但是使用Set数据类型可以为程序员提供在Array类型没有的程序执行过程中明显体现的功能。

数组和设置数据类型之间有什么区别?


Array数据类型的主要特征(我们将这种类型的对象称为“数组”)是数组是值的索引集合。 这意味着数组中的数据是使用索引存储的。

 const arr = [A, B, C, D]; console.log(arr.indexOf(A)); // : 0 console.log(arr.indexOf(C)); // : 2 

与数组不同, Set类型的对象(我们将其称为“集合”)是包含键/值格式的数据的集合。 集合不使用索引,而是使用键存储项目。 可以按添加到集合中的顺序对存储在集合中的元素进行排序,而集合不能存储相同的元素。 换句话说,集合的所有元素必须是唯一的。

这些收藏的主要优势是什么?


如果比较集合和数组,则可以发现一些优于集合而不是数组的优势,尤其是在程序性能很重要的情况下:

  • 搜索项目。 用于搜索元素并检查元素是否包含元素的数组方法indexOf()includes()缓慢。
  • 删除项目。 可以根据项目的值在集合中将其删除。 在数组中,此类操作的等效项是根据元素的索引使用splice()方法。 与项目搜索一样,使用索引删除项目是一项缓慢的操作。
  • 插入一个项目。 与在数组中使用push()unshift()类的方法相比,向集合中添加元素要快得多。
  • 使用NaN值。 不能使用indexOf()方法在数组中查找NaN值,而使用has()集合方法时,可以确定其中是否包含NaN
  • 删除重复项。 Set对象仅存储唯一值。 如果您需要避免在某些数据结构中保存重复元素,那么这是它们相对于数组的显着优势。 使用数组删除重复的元素时,必须编写其他代码。

可以在此处找到Set类型的对象的内置方法的完整列表。

关于算法的时间复杂度


数组用于搜索元素的方法具有线性时间复杂度-O(N)。 换句话说,元素搜索时间与数组的大小成正比。

与数组不同,集合用于查找,删除和添加元素的方法的时间复杂度为O(1)。 这意味着集合的大小实际上不会影响此类方法的工作时间。

在这里,您可以了解有关算法时间复杂度的更多信息。

集合比数组快多少?


尽管JavaScript代码的性能指标受到多种因素的强烈影响,但是它们尤其取决于代码运行的系统,所使用的代码运行时,所处理的数据的大小,我希望测试的结果能够为您提供比较数组和集合的机会从实际的角度出发,并了解集合如何比数组更快。 现在,我们将考虑三个简单的测试并分析其结果。

▍考试准备


在进行任何测试之前,让我们创建一个包含一百万个元素和相同集合的数组。 为了简单起见,我们将使用一个循环,其第一个计数器值为0,最后一个为-999999:

 let arr = [], set = new Set(), n = 1000000; for (let i = 0; i < n; i++) { arr.push(i); set.add(i); } 

▍测试号1:检查数组和集合中元素的存在


首先,我们预先知道数组和集合中元素123123的存在,因为它们已经存在于这些数据结构中。

 let result; console.time('Array'); result = arr.indexOf(123123) !== -1; console.timeEnd('Array'); console.time('Set'); result = set.has(123123); console.timeEnd('Set'); 

这是此测试的结果:

 Array: 0.173ms Set: 0.023ms 

集合比数组快7.54倍。

▍测试2:插入元素


现在,让我们尝试将元素添加到数组和集合中。

 console.time('Array'); arr.push(n); console.timeEnd('Array'); console.time('Set'); set.add(n); console.timeEnd('Set'); 

这是发生了什么:

 Array: 0.018ms Set: 0.003ms 

集合比数组快6.73倍。

▍测试3:删除项目


现在,让我们从每个数据结构中删除该项目(例如,在上一个测试中添加的项目)。 数组没有用于删除元素的内置方法,因此我们将创建一个辅助函数以使代码保持良好状态:

 const deleteFromArr = (arr, item) => { let index = arr.indexOf(item); return index !== -1 && arr.splice(index, 1); }; 

这是测试代码:

 console.time('Array'); deleteFromArr(arr, n); console.timeEnd('Array'); console.time('Set'); set.delete(n); console.timeEnd('Set'); 

结果如下:

 Array: 1.122ms Set: 0.015ms 

在这种情况下,集合比数组快74.13倍!

通常,应该指出,通过使用集合而不是数组,可以显着提高代码性能。 考虑一些实际的例子。

Example#1:从数组中删除重复的值


如果需要快速从数组中删除重复的值,可以将其转换为集合。 这也许是摆脱重复值的最简单方法:

 const duplicateCollection = ['A', 'B', 'B', 'C', 'D', 'B', 'C']; //       let uniqueCollection = new Set(duplicateCollection); console.log(uniqueCollection) // : Set(4) {"A", "B", "C", "D"} //        let uniqueCollection = [...new Set(duplicateCollection)]; console.log(uniqueCollection) // : ["A", "B", "C", "D"] 

示例2:Google的面试任务


在我的材料之一中我研究了回答Google采访者提出的问题的四个选项。 采访是使用C ++进行的,但是如果使用JavaScript代替此语言,则必须使用Set数据结构来解决问题。

如果您想更深入地了解该问题的答案,请阅读上述文章。 在这里,我只是展示一个现成的解决方案。

▍任务


给定一个未排序的整数数组和一个sum值。 如果通过添加此数组的任何两个元素而得到sum ,则编写一个返回true的函数。 如果数组中没有此类元素,则函数应返回false

事实证明,例如,如果给我们一个数组[3, 5, 1, 4]并且sum9 ,则该函数应返回true ,因为4+5=9

▍解决方案


您可以使用以下思路解决此问题:您需要遍历数组,在排序时创建Set数据结构,将在其中添加值以补充与sum找到的值。

让我们使用上述数组的示例来分析这个想法。 当我们遇到3 ,可以将数字6添加到集合中,因为我们知道需要找到两个总数为9数字。 然后,每次我们从数组中遇到一个新值时,我们都可以检查该集合并查看它是否存在。 当我们遇到数字5 ,我们会将4加到集合中。 最后,当我们到达数字4 ,便在集合中找到它并可以返回true

以下是此问题的解决方案:

 const findSum = (arr, val) => { let searchValues = new Set(); searchValues.add(val - arr[0]); for (let i = 1, length = arr.length; i < length; i++) {   let searchVal = val - arr[i];   if (searchValues.has(arr[i])) {     return true;   } else {     searchValues.add(searchVal);   } }; return false; }; 

这里是一个更简洁的解决方案:

 const findSum = (arr, sum) => arr.some((set => n => set.has(n) || !set.add(sum - n))(new Set)); 

由于Set.prototype.has()方法的时间复杂度为O(1),因此使用Set数据结构存储将数组中找到的数字补充为给定值的数字,可以找到线性时间(O(N))的解决方案。

如果该解决方案取决于Array.prototype.indexOf()方法或Array.prototype.includes()方法,每个方法的时间复杂度为O(N),则该算法的总时间复杂度将为O(N 2 )。 结果,他会变得慢很多。

总结


如果您以前从未在JavaScript中遇到过Set数据类型,那么我们希望现在有了一个想法,您将能够在您的项目中受益匪浅。

亲爱的读者们! 您将如何在代码中应用Set数据结构?

Source: https://habr.com/ru/post/zh-CN447578/


All Articles