当遇到大数据量处理时,前端的小姐姐、小哥哥们经常束手无策,然后小心翼翼去请教一些大神,大神们一般会看一眼,然后来一句: “脚本代码,动态语言,性能就那样,解决不了。”
但是事实真的解决不了吗? 未必。
性能的解决在于算法,无语言无必然关系,只要语言提供了支撑相应算法的特性,高性能就不再话下!
Part1. 上例子
闲话少叙,请看入下代码:
function show(html) { document.write(html + "<br><br>"); }
function DelRepeatFun1(arr) //函数1:采用IndexOf判断是否存在重复元素
{
var newarr = [];
var len = arr.length;
for(var i=0; i<len; i++) { if(newarr.indexOf(arr[i])==-1) { newarr.push(arr[i]); } }
return newarr.length;
}
function DelRepeatFun2(arr) //函数2:对象属性的机制,默认去重
{
var newarrobj = {};
var len = arr.length;
for(var i=0; i<len; i++) { newarrobj[arr[i]]= 1; }
var newarr = [];
for(var n in newarrobj) { newarr.push(n); }
return newarr.length;
}
function Test()
{
var arr = [];
//先构造一个20万级的数据量
for(var i=0; i<200000; i++) { arr.push( Math.floor(Math.random() * 100000) ); }
var t1 = (new Date()).getTime();
var arrlen1 = DelRepeatFun1(arr); //采用indexOf 去重
var ms1 = ((new Date()).getTime() - t1);
show("IndexOf函数去重耗时:" + ms1/1000.00 + "秒, 原数组长度: " + arr.length + ", 去重后数组长度:" + arrlen1)
var t2= (new Date()).getTime();
var arrlen2 = DelRepeatFun2(arr); //采用对象属性机制去重
var ms2 = ((new Date()).getTime() - t2) ;
show("对象属性特性去重耗时:" + ms2/1000.00 + "秒, 原数组长度: " + arr.length + ", 去重后数组长度:" + arrlen2)
show("<br>性能相差<B>" + (ms1/ms2).toFixed(2) + "</B>了倍!")
}
Test(); //开始执行测试
以上是一个对20万级数组的去重,采用了2种方式去实现。
Part2. 看结果
Chrome测试结果:
IE11 测试结果:
(注:忘了注明页面编码,所以IE很智能的呈现了乱码,但实在不想运行第2遍了,太慢了,就截图标注示意一下,请原谅我的敷衍。)
Part3. 说原因
相同的数据,性能相差其实非常大,而且数据量级再往上提到100万级,方法2依旧不费力,但是方法1就卡住了,那原因是什么呢?
原因就是:
方法1:采用的是遍历查找, indexOf函数内部依旧是通过遍历循环查找判断是否存在,那么整个数据循环次数其实等价于 20万 * 20万 = 400亿。
方法2:采用的是类似哈希查找,属性存储的机制应该采用的是类似哈希表的机制, 查找方式类似于我们常说的2分法之流(推测哈,实际上的算法应该不是我们想象中那种傻白甜的二分法), 在20万数据中用二分法锁定一个数,要多少次呢?
2^17 = 262144 (26.2万)
2的17次方数就超过了20万,所以理论上分17次就能在20万中锁定一个数,那么全部循环次数等于:
20万*17 = 340万
对比: 方法一循环 400亿, 方法2循环340万 , 二者理论相差: 11764 倍, 所以你说该谁快?