js数组去重效率测试
最近一次面试遇到了一个问题,用原生js至少实现三种数思路,巴拉巴拉…,描述完毕.另一个问题又来了,请分析一下哪一种的效率最高,当时我就想,应该越是原生的代码,效率应该越高吧.然后就现实并非如此,面试自然也就无疾而终,然后我回家做了一番测试,结果如下
1 双层遍历
default: 7.007080078125ms
2 排序去重
default: 43.743896484375ms
3 对象属性去重
default: 3.615966796875ms
4 indexOf去重
default: 8.56396484375ms
5 includes去重
default: 8.193115234375ms
6 filter过滤检测
default: 8.48291015625ms
7 forEach去重
default: 8.1611328125ms
8 lastIndexOf去重
default: 3.82177734375ms
9 set方法
default: 1.489990234375ms
下面是测试步骤
首先我创建了一个大数组
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;
const arr1 = Array.from(Array(10000), (v,k) => ({a: random(0, 10000)}));
网上找了一些方法 ,用了这位博主的 [https://www.jb51.net/article/121410.htm] 感谢
// Methods 1 双层遍历
function unique(arr){
var res = [arr[0]];
for(var i=1; i<arr.length; i++){
var repeat = false;
for(var j=0; j<res.length; j++){
if(arr[i] === res[j]){
repeat = true;
break;
}
}
if(!repeat){
res.push(arr[i]);
}
}
return res;
}
// Methods 2 排序去重
function unique2(arr){
var arr2 = arr.sort();
var res = [arr2[0]];
for(var i=1; i<arr2.length; i++){
if(arr2[i] !== res[res.length-1]){
res.push(arr2[i]);
}
}
return res;
}
// Methods 3 对象属性去重
function unique3(arr){
var res = [];
var obj = {};
for(var i=0; i<arr.length; i++){
if( !obj[arr[i]] ){
obj[arr[i]] = 1;
res.push(arr[i]);
}
}
return res;
}
// Methods 4 indexOf去重
function unique4(arr){
var res = [];
for(var i=0; i<arr.length; i++){
if(res.indexOf(arr[i]) == -1){
res.push(arr[i]);
}
}
return res;
}
// Methods 5 includes检测去重
function unique5(arr){
var res = [];
for(var i=0; i<arr.length; i++){
if( !res.includes(arr[i]) ){ // 如果res新数组包含当前循环item
res.push(arr[i]);
}
}
return res;
}
// Methods 6 filter过滤检测
function unique6(arr){
var res = [];
res = arr.filter(function(item){
return res.includes(item) ? '' : res.push(item);
});
return res;
}
// Methods 7 forEach去重
function unique7(arr){
var res = [];
arr.forEach(function(item){
res.includes(item) ? '' : res.push(item);
});
return res;
}
// Methods 8 lastIndexOf去重
function unique8(arr){
var res = [];
for(var i=0; i<arr.length; i++){
res.lastIndexOf(arr[i]) !== -1 ? '' : res.push(arr[i]);
}
return res;
}
// Methods 9 Set去重
function unique9(arr){
return Array.from(new Set(arr));
}
打印测试
// 1 双层遍历
console.log("1 双层遍历")
console.time()
unique(arr1)
console.timeEnd();
// 2 排序去重
console.log("2 排序去重")
console.time()
unique2(arr1)
console.timeEnd();
// 3 对象属性去重
console.log("3 对象属性去重")
console.time()
unique3(arr1)
console.timeEnd();
// 4 indexOf去重
console.log("4 indexOf去重")
console.time()
unique4(arr1)
console.timeEnd();
// 5 includes去重
console.log("5 includes去重")
console.time()
unique5(arr1)
console.timeEnd();
// 6 filter过滤检测
console.log("6 filter过滤检测")
console.time()
unique6(arr1)
console.timeEnd();
// 7 forEach去重
console.log("7 forEach去重")
console.time()
unique7(arr1)
console.timeEnd();
// 8 lastIndexOf去重
console.log("8 lastIndexOf去重")
console.time()
unique8(arr1)
console.timeEnd();
// 9 set方法
console.log(" 9 set方法")
console.time()
unique9(arr1)
console.timeEnd();
结论:
由以上的结果我们可以知道,其实es6去重方法set()效率还是最高的,究其原因,大概是底层的c源码实现的缘故.所以在不考虑兼容的前提下,大家放心大胆的用new Set()方法就行了,假如需要考虑兼容问题,indexOf也是个不错的选择,此方法也是最常用最容易理解的。
2021年-6-25 结论修改:
new Set
的效率在一万条数据的情况下,确实是最快的。但是当数据达到百万千万级别,for
循环 结合对象属性的唯一性来实现,效率则更高。研究了几个ES6的api效率的问题,包括 filter
、Map
、reduce
等方法,它们在一万条数据计算的情况下,效率优于其他方案。同样,当数据量达到百万千万级别时,for
循环的一些实现方案则效率更优。所以我们在做js优化的过程中,应该考虑对于不同的数据量选用不同的优化方案才能更好的做好优化。顺带提一句,indexOf
在大量的数据处理过程中会极大的降低代码性能,所以在大量数据处理的代码中,我们应该避免使用它。
贴一下以上结论的部分测试代码,有兴趣的可自行编码测试
1万,100万,
/**
* 研究数组对象去重的效率问题
* 对数组进行去重
*/
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;
const list = Array.from(Array(10000), (v,k) => random(0, 100000 ));
console.time("Set");
let arr = Array.from(new Set(list));
console.timeEnd("Set");
console.time("for");
let obj1 = {};
let data1 = [];
for(let i = 0, len = list.length; i < len; i++){
const item = list[i];
if( obj1[item] === undefined ){
obj1[item] = item;
data1.push(item);
}
}
console.timeEnd("for");
测试结果
1万条数据
Set: 0.512939453125 ms
for: 4.902099609375 ms
100万条数据
Set: 30.05615234375 ms
for: 17.64111328125 ms