為什么前端開發者都用 Set 代替 Array 去重?實測性能對比
數組去重是JavaScript中的一個常見的操作,隨著ES6的普及,越來越多的前端開發者拋棄了傳統的Array去重方法,轉而使用Set來完成這項任務。這種轉變不僅僅是因為代碼更簡潔,更重要的是性能上的巨大差異。
一、Set去重的簡潔寫法
在ES6出現之前,數組去重通常需要編寫循環和條件判斷:
function uniqueArray(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i]);
}
}
return result;
}
// 使用
const array = [1, 2, 3, 3, 4, 4, 5];
const unique = uniqueArray(array); // [1, 2, 3, 4, 5]
而使用Set,代碼可以簡化為:
但代碼簡潔只是表面優勢,真正的價值在于性能提升。
二、性能對比:數字以為自己聽錯了
讓我們通過幾種常見的去重方法進行性能對比:
- Set方法
- 傳統的indexOf方法
- 使用對象(Object)作為哈希表
- 使用Array.includes方法
- 使用filter + indexOf
三、測試方法
我們將創建不同大小的數組進行測試,每個數組包含隨機生成的數字,并確保約有30%的元素是重復的:
四、測試結果
以下是在不同大小數組上各方法的執行時間(單位:毫秒):
方法 | 100元素 | 10,000元素 | 1,000,000元素 |
Set | 0.05 | 1.2 | 85 |
Object哈希表 | 0.08 | 2.8 | 120 |
indexOf | 0.2 | 350 | 超過30秒 |
includes | 0.2 | 380 | 超過30秒 |
filter+indexOf | 0.3 | 800 | 超過60秒 |
在百萬級數據上,Set比傳統indexOf方法快了約300倍以上。
五、為什么Set如此高效?
Set之所以能提供如此驚人的性能優勢,主要有以下幾個原因:
1. 數據結構的本質區別
Set是基于哈希表實現的,這意味著:
- 查找、添加和刪除操作的時間復雜度為O(1)
- 每個值在底層都有唯一的"地址",可以直接訪問
而Array的indexOf和includes方法需要線性搜索,時間復雜度為O(n)。
2. 引擎優化
JavaScript引擎對Set進行了特殊優化:
- V8引擎中,Set使用哈希表和紅黑樹的組合實現
- Set在內存中的布局更適合現代CPU的緩存機制
- 引擎可以對Set操作應用更多底層優化
3. 自動處理邊緣情況
Set能正確處理JavaScript中的特殊值:
注意Set正確地將NaN與NaN視為相同(盡管NaN !== NaN),并且區分了0和"0"。
六、什么時候不應該使用Set?
盡管Set有許多優勢,但也不是所有場景都適合:
- 需要保持原始順序:雖然現代瀏覽器中Set是有序的(按插入順序),但這并不是規范保證的
- 需要索引訪問:Set不支持索引訪問(如set[0])
- 需要頻繁修改:如果需要頻繁修改集合中的元素,數組的API可能更方便
- 處理非原始類型:對于對象等非原始類型,Set使用引用相等,可能不符合預期
七、最佳實踐:Set和Array結合使用
現代前端開發中,一個常見的模式是Set和Array結合使用:
// 數據處理流程
constprocessData = (dataArray) => {
// 1. 去重
const uniqueData = [...newSet(dataArray)];
// 2. 使用數組方法進行處理
return uniqueData
.filter(item => item > 10)
.map(item => item * 2)
.sort((a, b) => a - b);
};
這種模式結合了Set的高效去重能力和Array豐富的數據處理方法。