性能对比:为什么 Set.has() 比 Array.includes() 更快?
在 JavaScript
开发中,检查某个元素是否存在于集合中是一个常见的操作。对于这个任务,我们通常会使用两种方法:Set.has()
和 Array.includes()
。尽管它们都能实现查找功能,但在性能上存在显著差异。今天我们就来探讨一下,为什么 Set.has()
通常比 Array.includes()
更快,特别是在查找大量元素时。
数据结构的差异:
Set
vsArray
首先,要理解性能差异,我们需要了解
Set
和Array
在JavaScript
中的底层实现原理。它们使用了不同的数据结构,这对查找操作的效率有着直接影响。Set
:哈希表的魔力Set
是一种集合数据结构,旨在存储唯一的值。JavaScript
中的Set
通常使用 哈希表 来实现。在哈希表中,每个元素都有一个唯一的哈希值,这个哈希值用于快速定位和访问该元素。这意味着,当我们使用Set.has()
来检查某个元素时,JS
引擎能够直接计算该元素的哈希值,从而迅速确定元素是否存在。查找操作的时间复杂度是O(1)
,即无论集合中有多少个元素,查找的时间几乎是恒定的。Array
:顺序遍历与
Set
不同,Array
是一种有序的列表结构,元素按插入顺序排列。在数组中查找元素时,Array.includes()
方法必须遍历数组的每一个元素,直到找到目标元素或确认元素不存在。这样,查找操作的时间复杂度是O(n)
,其中n
是数组中元素的个数。也就是说,随着数组中元素数量的增加,查找所需的时间将线性增长。
性能差异:什么时候该用哪个?
在实际开发中,我们通常会选择根据数据的特性来选择
Set.has()
或Array.includes()
。但是,理解它们的性能差异有助于我们做出更加明智的决策。小型数据集
对于较小的集合,性能差异可能不那么明显。在这种情况下,无论是
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
,两者的性能差异几乎不明显。Set.has(Code 1)和 Array.includes(Code 2)代码性能分析。数据来源:CodePerf
大型数据集
当数据集变得更大时,
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()
的执行时间会随着数组的大小而显著增加。Set.has(Code 1)和 Array.includes(Code 2)代码性能分析。数据来源:CodePerf
重复元素的影响
Set
本身就是一个集合,只允许存储唯一的元素,因此它天然会去除重复的元素。如果你在一个包含大量重复元素的数组中查找某个值,使用Set
可以提高性能。因为在将数组转换为Set
后,我们不必担心查找操作的冗余计算。// 数组中有重复元素
const arrayWithDuplicates = [1, 2, 3, 1, 2, 3];
const uniqueSet = new Set(arrayWithDuplicates);
// 使用 Set 查找
console.log(uniqueSet.has(2)); // true
何时选择
Array.includes()
尽管
Set.has()
在查找时的性能更优,但这并不意味着Array.includes()
就没有用武之地。对于小型数据集、对顺序有要求或需要保留重复元素的场景,Array.includes()
仍然是一个非常合适的选择。例如,数组保持元素的插入顺序,或者你需要查找重复元素时,数组仍然是首选。总结
Set.has()
性能较好,特别是在处理大型数据集时,其查找时间接近O(1)
。Array.includes()
在小型数据集或元素顺序敏感时可以正常工作,但随着数据量的增加,其时间复杂度为O(n)
。- 在需要频繁查找元素且数据量较大的情况下,建议使用
Set
。 - 对于较小数据集或有顺序要求的操作,
Array.includes()
仍然是一个合适的选择。 - 因为构造
Set
的过程本身就是遍历的过程,所以如果只用来查询一次的话,可以使用Array.includes()
。但如果需要频繁查询,则建议使用Set
,尤其是在处理较大的数据集时,性能优势更加明显。
通过理解这两种方法的性能差异,我们可以在编写
JavaScript
程序时更加高效地处理数据查找操作,选择合适的数据结构来提升应用的性能。
来源:juejin.cn/post/7433458585147342882