注册
web

性能对比:为什么 Set.has() 比 Array.includes() 更快?

JavaScript 开发中,检查某个元素是否存在于集合中是一个常见的操作。对于这个任务,我们通常会使用两种方法:Set.has()Array.includes()。尽管它们都能实现查找功能,但在性能上存在显著差异。今天我们就来探讨一下,为什么 Set.has() 通常比 Array.includes() 更快,特别是在查找大量元素时。




  1. 数据结构的差异:Set vs Array


    首先,要理解性能差异,我们需要了解 SetArrayJavaScript 中的底层实现原理。它们使用了不同的数据结构,这对查找操作的效率有着直接影响。




    1. Set:哈希表的魔力

      Set 是一种集合数据结构,旨在存储唯一的值。JavaScript 中的 Set 通常使用 哈希表 来实现。在哈希表中,每个元素都有一个唯一的哈希值,这个哈希值用于快速定位和访问该元素。这意味着,当我们使用 Set.has() 来检查某个元素时,JS 引擎能够直接计算该元素的哈希值,从而迅速确定元素是否存在。查找操作的时间复杂度是 O(1) ,即无论集合中有多少个元素,查找的时间几乎是恒定的。



    2. Array:顺序遍历

      Set 不同,Array 是一种有序的列表结构,元素按插入顺序排列。在数组中查找元素时,Array.includes() 方法必须遍历数组的每一个元素,直到找到目标元素或确认元素不存在。这样,查找操作的时间复杂度是 O(n) ,其中 n 是数组中元素的个数。也就是说,随着数组中元素数量的增加,查找所需的时间将线性增长。





  2. 性能差异:什么时候该用哪个?


    在实际开发中,我们通常会选择根据数据的特性来选择 Set.has()Array.includes()。但是,理解它们的性能差异有助于我们做出更加明智的决策。




    1. 小型数据集

      对于较小的集合,性能差异可能不那么明显。在这种情况下,无论是 Set.has() 还是 Array.includes(),都能以接近常数时间完成操作,因为数据集本身就很小。因此,在小数据集的情况下,开发者更关心的是易用性和代码的简洁性,而不是性能。


      例如,以下是对小型数据集的查找操作:


      // 小型数据集
      const smallSet = new Set([1, 2, 3, 4, 5]);
      console.log(smallSet.has(3));  // true

      const smallArray = [1, 2, 3, 4, 5];
      console.log(smallArray.includes(3));  // true

      在这个示例中,Set.has()Array.includes() 都能快速找到元素 3,两者的性能差异几乎不明显。


      image-20241105095232819


      Set.has(Code 1)和 Array.includes(Code 2)代码性能分析。数据来源:CodePerf



    2. 大型数据集

      当数据集变得更大时,Set.has() 的优势变得尤为明显。如果我们使用 Array.includes() 在一个包含上百万个元素的数组中查找一个目标元素,时间复杂度将变为 O(n) ,查找时间会随着数组的大小而增长。


      Set.has() 在面对大数据集时,性能依然保持在 O(1) ,因为它利用了哈希表的高效查找特性。下面是两个在大数据集下性能对比的例子:


      // 大型数据集
      const largeArray = Array.from({ length: 1000000 }, (_, i) => i);
      const largeSet = new Set(largeArray);

      const valueToFind = 999999;

      console.time("Set.has");
      console.log(largeSet.has(valueToFind));  // true
      console.timeEnd("Set.has");

      console.time("Array.includes");
      console.log(largeArray.includes(valueToFind));  // true
      console.timeEnd("Array.includes");

      在这个例子中,当数据集非常大时,Set.has() 显示了明显的性能优势,而 Array.includes() 的执行时间会随着数组的大小而显著增加。


      BIG


      Set.has(Code 1)和 Array.includes(Code 2)代码性能分析。数据来源:CodePerf



    3. 重复元素的影响

      Set 本身就是一个集合,只允许存储唯一的元素,因此它天然会去除重复的元素。如果你在一个包含大量重复元素的数组中查找某个值,使用 Set 可以提高性能。因为在将数组转换为 Set 后,我们不必担心查找操作的冗余计算。


      // 数组中有重复元素
      const arrayWithDuplicates = [1, 2, 3, 1, 2, 3];
      const uniqueSet = new Set(arrayWithDuplicates);

      // 使用 Set 查找
      console.log(uniqueSet.has(2));  // true





  3. 何时选择 Array.includes()


    尽管 Set.has() 在查找时的性能更优,但这并不意味着 Array.includes() 就没有用武之地。对于小型数据集、对顺序有要求或需要保留重复元素的场景,Array.includes() 仍然是一个非常合适的选择。例如,数组保持元素的插入顺序,或者你需要查找重复元素时,数组仍然是首选。



  4. 总结



    1. Set.has() 性能较好,特别是在处理大型数据集时,其查找时间接近 O(1)
    2. Array.includes() 在小型数据集或元素顺序敏感时可以正常工作,但随着数据量的增加,其时间复杂度为 O(n)
    3. 在需要频繁查找元素且数据量较大的情况下,建议使用 Set
    4. 对于较小数据集或有顺序要求的操作,Array.includes() 仍然是一个合适的选择。
    5. 因为构造 Set 的过程本身就是遍历的过程,所以如果只用来查询一次的话,可以使用 Array.includes()。但如果需要频繁查询,则建议使用 Set,尤其是在处理较大的数据集时,性能优势更加明显。

    通过理解这两种方法的性能差异,我们可以在编写 JavaScript 程序时更加高效地处理数据查找操作,选择合适的数据结构来提升应用的性能。



作者:一点一木
来源:juejin.cn/post/7433458585147342882

0 个评论

要回复文章请先登录注册